欢迎关注我的公众号睿Talk,获取我最新的文章:一、前言当使用 React 开发系统的时候,常常需要配置很多繁琐的参数,如 Webpack 配置、Router 配置和服务器配置等。如果需要做 SEO,要考虑的事情就更多了,怎么让服务端渲染和客户端渲染保持一致是一件很麻烦的事情,需要引入很多第三方库。针对这些问题,Next.js提供了一个很好的解决方案,使开发人员可以将精力放在业务上,从繁琐的配置中解放出来。下面我们一起来看看它的一些特性。二、特性介绍Next.js 具有以下几点特性:默认支持服务端渲染自动根据页面进行代码分割简洁的客户端路由方案(基于页面)基于 Webpack 的开发环境,支持热模块替换可以跟 Express 或者其它 Node.js 服务器完美集成支持 Babel 和 Webpack 的配置项定制三、Hello World执行以下命令,开始 Next.js 之旅:mkdir hello-nextcd hello-nextnpm init -ynpm install –save react react-dom nextmkdir pages在package.json中输入以下内容:{ “scripts”: { “dev”: “next”, “build”: “next build”, “start”: “next start” }}在 pages 文件夹下,新建一个文件 index.js:const Index = () => ( <div> <p>Hello Next.js</p> </div>)export default Index在控制台输入npm run dev,这时候在浏览器输入http://localhost:3000,就能看到效果了。四、路由Next.js 没有路由配置文件,路由的规则跟 PHP 有点像。只要在 pages 文件夹下创建的文件,都会默认生成以文件名命名的路由。我们新增一个文件看效果pages/about.jsexport default function About() { return ( <div> <p>This is the about page</p> </div> )}在浏览器输入http://localhost:3000/about,就能看到相应页面了。如果需要进行页面导航,就要借助next/link组件,将 index.js 改写:import Link from ’next/link’const Index = () => ( <div> <Link href="/about"> <a>About Page</a> </Link> <p>Hello Next.js</p> </div>)export default Index这时候就能通过点击链接进行导航了。如果需要给路由传参数,则使用query string的形式: <Link href="/post?title=hello"> <a>About Page</a> </Link>取参数的时候,需要借助框架提供的withRouter方法,参数封装在 query 对象中:import { withRouter } from ’next/router’const Page = withRouter(props => ( <h1>{props.router.query.title}</h1>))export default Page如果希望浏览器地址栏不显示query string,可以使用as属性:<Link as={/p/${props.id}} href={/post?id=${props.id}} <a>{props.title}</a></Link>这时候浏览器会显示这样的url:localhost:3000/p/12345五、SSRNext.js 对服务端渲染做了封装,只要遵守一些简单的约定,就能实现 SSR 功能,减少了大量配置服务器的时间。以上面这个 url 为例子,直接在浏览器输入localhost:3000/p/12345是会返回404的,我们需要自己实现服务端路由处理的逻辑。下面以express为例子进行讲解。新建一个 server.js 文件:const express = require(’express’)const next = require(’next’)const dev = process.env.NODE_ENV !== ‘production’const app = next({ dev })const handle = app.getRequestHandler()app .prepare() .then(() => { const server = express() // 处理localhost:3000/p/12345路由的代码 server.get(’/p/:id’, (req, res) => { const actualPage = ‘/post’ const queryParams = { title: req.params.id } app.render(req, res, actualPage, queryParams) }) server.get(’*’, (req, res) => { return handle(req, res) }) server.listen(3000, err => { if (err) throw err console.log(’> Ready on http://localhost:3000’) }) }) .catch(ex => { console.error(ex.stack) process.exit(1) })当遇到/p/:id这种路由的时候,会调用app.render方法渲染页面,其它的路由则调用app.getRequestHandler方法。无论是服务端渲染还是客户端渲染,往往都需要发起网络请求获取展示数据。如果要同时考虑 2 种渲染场景,可以用getInitialProps这个方法:import Layout from ‘../components/MyLayout.js’import fetch from ‘isomorphic-unfetch’const Post = props => ( <Layout> <h1>{props.show.name}</h1> <p>{props.show.summary.replace(/<[/]?p>/g, ‘’)}</p> <img src={props.show.image.medium} /> </Layout>)Post.getInitialProps = async function(context) { const { id } = context.query const res = await fetch(https://api.tvmaze.com/shows/${id}) const show = await res.json() console.log(Fetched show: ${show.name}) return { show }}export default Post获取数据后,组件的props就能获取到getInitialProps return 的对象,render 的时候就能直接使用了。getInitialProps是组件的静态方法,无论服务端渲染还是客户端渲染都会调用。如果需要获取 url 带过来的参数,可以从context.query里面取。六、CSS in JS对于页面样式,Next.js 官方推荐使用 CSS in JS 的方式,并且内置了styled-jsx。用法如下:import Layout from ‘../components/MyLayout.js’import Link from ’next/link’function getPosts() { return [ { id: ‘hello-nextjs’, title: ‘Hello Next.js’ }, { id: ’learn-nextjs’, title: ‘Learn Next.js is awesome’ }, { id: ‘deploy-nextjs’, title: ‘Deploy apps with ZEIT’ } ]}export default function Blog() { return ( <Layout> <h1>My Blog</h1> <ul> {getPosts().map(post => ( <li key={post.id}> <Link as={/p/${post.id}} href={/post?title=${post.title}}> <a>{post.title}</a> </Link> </li> ))} </ul> <style jsx>{ h1, a { font-family: 'Arial'; } ul { padding: 0; } li { list-style: none; margin: 5px 0; } a { text-decoration: none; color: blue; } a:hover { opacity: 0.6; } }</style> </Layout> )}注意<style jsx>后面跟的是模板字符串,而不是直接写样式。七、导出为静态页面如果网站都是简单的静态页面,不需要进行网络请求,Next.js 可以将整个网站导出为多个静态页面,不需要进行服务端或客户端动态渲染了。为了实现这个功能,需要在根目录新建一个next.config.js配置文件:module.exports = { exportPathMap: function() { return { ‘/’: { page: ‘/’ }, ‘/about’: { page: ‘/about’ }, ‘/p/hello-nextjs’: { page: ‘/post’, query: { title: ‘Hello Next.js’ } }, ‘/p/learn-nextjs’: { page: ‘/post’, query: { title: ‘Learn Next.js is awesome’ } }, ‘/p/deploy-nextjs’: { page: ‘/post’, query: { title: ‘Deploy apps with Zeit’ } } } }}这个配置文件定义了 5 个需要导出的页面,以及这些页面对应的组件和需要接收的参数。然后在package.json定义下面 2 个命令,然后跑一下:{ “scripts”: { “build”: “next build”, “export”: “next export” }}npm run buildnpm run export跑完后根目录就会多出一个out文件夹,所有静态页面都在里面。八、组件懒加载Next.js 默认按照页面路由来分包加载。如果希望对一些特别大的组件做按需加载时,可以使用框架提供的next/dynamic工具函数。import dynamic from ’next/dynamic’const Highlight = dynamic(import(‘react-highlight’))export default class PostPage extends React.Component { renderMarkdown() { if (this.props.content) { return ( <div> <Highlight innerHTML>{this.props.content}</Highlight> </div> ) } return (<div> no content </div>); } render() { return ( <MyLayout> <h1>{this.props.title}</h1> {this.renderMarkdown()} </MyLayout> ) } }}当 this.props.content 为空的时候,Highlight 组件不会被加载,加速了页面的展现,从而实现按需加载的效果。九、总结本文介绍了 Next.js 的一些特性和使用方法。它最大的特点是践行约定大于配置思想,简化了前端开发中一些常用功能的配置工作,包括页面路由、SSR 和组件懒加载等,大大提升了开发效率。更详细的使用介绍请看官方文档。