"scripts": {
"dev": "node server.js",
"build": "next build",
"start": "NODE_ENV=production pm2 start server.js",
"stop": "pm2 stop server.js"
}
1、下载好项目后,安装依赖。
npm install 或者cnpm install
2、启动项目。
npm run dev
3、开发完成后,生产环境测试。
npm run build // 先打包
npm start // 再启动
4、检测没有问题,部署到服务器。
以阿里云为例,react服务端架构部署的基本步骤是,使用各种方法将前端项目上传到服务端,可以是FTP上传,也 可以在服务器使用git clone下载项目。
你得在服务器安装好node环境,然后和上面的第一步一样,在服务端安装依赖,然后就不要用dev启动项目,而是start,start设置成pm2启动,这样你的项目就能在服务端一直运行。
通常将前端项目放到和后端同内网的服务器上,这样的话,初始渲染需要请求的接口是在内网直接请求,首屏渲染的速度会更快。
Next框架和客户端框架的目录结构有所不同,需要注意这么几个关键的文件夹。
1、pages
pages下面有多个js文件,每个js文件对应一个服务端路由。index.js作为默认入口,也是首页路由。
2、static
static是静态资源的路径,这是Next采用的资源文件的管理方式,通常我将每个路由建立一个静态文件夹来管理。如果你将静态资源放到组件下的文件夹管理,可能会报路径的错误。
3、redux
目前redux目录下面只有一个store.js文件,在客户端开发的时候,是将reducer、store、action分开来管理,在服务端也可以学习这种管理模式。
4、styles
公共css资源
5、utils
定义各种接口的工具函数
6、components
每个路由使用到的组件都放在该文件夹下面,也是你开发react组件最常访问的地方
拿到一个新框架开发前端,通常就是写js、写css、然后关联起来,再导入一些图片,只要熟悉这些步骤,Next不会成为你的阻碍。
我拿首页路由index.js来讲解一下如何写服务端渲染的路由。
下面这个例子的写法相信对你来说一点也不陌生,他看起来就是一个纯客户端组件。它只做了一件事情,渲染一个Home组件。 前面我们说它是个路由,现在看起来就是一个组件,和路由有什么关系呢?下面讲到路由的跳转时你会有所体会。
import React from 'react' // 导入react
import { initStore } from '../redux/store' // 导入store,服务端渲染要初始化store
import withRedux from 'next-redux-wrapper' //这个插件和客户端不一样,需要特别注意
import Layout from layout // 在这里我提取了一个容器组件
import Home from '../components/Home' //导入首页组件
class App extends React.Component {
componentDidMount () { }
componentWillUnmount () { }
render () {
return (
<Layout title={`图书商城`}>
<Home />
</Layout>
)
}
}
const mapDispatchToProps = (dispatch) => {
return {}
}
export default withRedux(initStore, null, mapDispatchToProps)(App)
在首页的路由中,我们导入了Home组件,也就是首页组件,如果说路由组件中redux插件和客户端有些许不同,但是在首页组件中,写法就和客户端的完全一样。 所以使用Next开发,完全不用担心写法问题,只要你会客户端的react,写起服务端就很轻松。
import React from 'react' //关键
import { connect } from 'react-redux' //关键
import { bindActionCreators } from 'redux' //关键
import Header from './Header'
import Nav from './Nav'
import Special from './Special'
import { getNav } from '/redux/store' //关键
class Home extends React.Component {
constructor(props) {
super(props)
}
componentWillMount() {
const { navMain=[] } = this.props
if (navMain.length == 0) {
this.props.getNav()
}
}
render() {
let bgClass = { background: '#00bb9c' }
const { navMain=[] } = this.props
return (
<div>
<Header
title="react-next服务端渲染"
linkTo="/search"
bgColor={bgClass}
imgUrl={`/static/home/search.svg`}
/>
<div className="style_div">
<ul className="style_ul">
{
navMain.map((elem, index) => {
return (
<Nav
key={index}
title={elem.text}
index={index}
handleClick={() => this.handleClick()}
/>
)
})
}
</ul>
</div>
<Special />
</div>
)
}
}
const mapStateToProps = ({ navMain }) => ({ navMain })
const mapDispatchToProps = (dispatch) => {
return {
getNav: bindActionCreators(getNav, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Home)
上面的Home组件中,有一个子组件Header,我在Header实现了一个Link标签,这个Link不是客户端中react-router的Link,而是 next/link的Link,服务端路由和客户端路由使用的插件不一样,你可以忘记客户端的react-router了。
import React from 'react'
import Link from 'next/link'
import Head from 'next/head'
import PropTypes from 'prop-types'
import headerStyle from './styles/header.scss'
const Header = (props) => {
const { title, imgUrl, linkTo, bgColor, handleClick } = props
return (
<header className='header' style={bgColor}>
<Head><style dangerouslySetInnerHTML={{__html: headerStyle}}></style></Head>
{title}
<Link href={linkTo}>
<a className="a_link" onClick={() => handleClick('left')}><img src={imgUrl || false} className="a_img" /></a>
</Link>
</header>
)
}
Header.propTypes = {
title: PropTypes.string,
imgUrl: PropTypes.string,
linkTo: PropTypes.string,
bgColor: PropTypes.object
}
export default Header
注意这个Link的DOM写法,不是使用to,而是href,内部再嵌入一个a标签。href中的linkTo是 '/search',上面的代码有提到。 然后你是否会想,search路由在哪里,因为我们没有写<Route path="/search" render={() => } />,其实很简单, Next已经在底层帮我们做好了路由的处理,你只要在pages目录下面加上search.js路由就行,具体看代码。然后在任意组件的 内部都可以通过Link或者Next提供的路由插件处理跳转。
<Link href={linkTo}>
<a className="a_link" onClick={() => handleClick('left')}><img src={imgUrl || false} className="a_img" /></a>
</Link>
1、还是看Header组件,本项目采用scss组件的写法,默认是支持css的,导入的方式如下:
import headerStyle from './styles/header.scss'
2、Next更重要的一个特性是支持组件内部css的写法。
import React from 'react'
import PropTypes from 'prop-types'
const HotSearch = (props) => {
const { hotText, hotClick, currentHot } = props
return (
<a className={`style_span`}
onClick={hotClick}
style={{color: currentHot === hotText ? '#398cee' : ''}}
>
{hotText}
<style jsx>{`
.style_span {
display: inline-block;
background: #f4f4f4;
padding: 0.41667rem 0.833rem;
margin-right: 1rem;
margin-bottom: 0.833rem;
color: #444;
font-size: 1rem;
}`}</style>
</a>
)
}
HotSearch.propTypes = {
hotText: PropTypes.string,
hotClick: PropTypes.func,
currentHot: PropTypes.string
}
export default HotSearch
特别注意,导入图片的方式不再是require或者import了,因为服务端本身的资源都是路径明确的,所以采用传统的html导入图片的写法。而且图片资源必须放在static目录下面,否则就会报错,至于能不能放在 其他目录下面,我还没尝试成功。
<img src={`/static/search/search_1.png`} className="style_div_img" />
这个就不用说了吧,和客户端的写法一样。如果你觉得redux-sage更好,那就换成它。
next.config.js文件支持自定义webpack配置。
server.js的配置也很丰富,你可以前往Next官网学习。