最近在写组件库,关注到了 Next.js,项目中也正在使用,现在把它整理成文档记录一下,也希望可以帮助需要的同学。
那么,什么是 Next.js ?
它是一个 React 开发框架。
通常使用 React 开发项目时,需要做配置打包、编译等等复杂繁琐的工作。Next.js 提供了一个解决方案,开发人员可以把精力放在业务上,而不必关心项目的配置。
它能解决什么问题?
自动编译和打包
热加载
预渲染、客户端渲染、服务器渲染
静态和动态路由
静态资源
代码拆分
它有什么特点?
基于页面的路由方案(/pages)
预渲染,支持在页面级的 静态生成 (SSG) 和 服务器端渲染 (SSR)
自动根据页面进行代码分割
内置 CSS 、 Sass 、.less 和 .styl 的支持,并支持任何 CSS-in-JS 库
支持定制 Babel 和 Webpack 的配置项
支持热更新
利用 Serverless Functions 及 API 路由 构建 API 功能
如何创建新应用并使用呢?
创建应用
npx create-next-app nextjs-demo --use-npm --example "https://github.com/vercel/next-learn-starter/tree/master/learn-starter"
上面命令的主要作用是通过调用
create-next-app
工具来创建一个 Next.js 项目。通过--example
参数指定使用 此模板 。
运行应用
首先,进行文件目录:
cd nextjs-demo
其次,启动应用:
npm run dev
这时,在浏览器打开 http://localhost:3000/
来运行应用。
编辑页面
首先,找到并打开 /pages/index.js 页面。
其次,修改标题,Welcome to 改成 Learn 并保存。
最后,查看页面(Welcome to 已改成 Learn)。
路由
Next.js 没有路由配置文件,采用的是约定式路由。即 pages 文件夹下创建的文件,都会默认生成以文件名命名的路由。
例如:
pages/index.js 代表的是入口 / 路由。
pages/about/policy.js 代表的是 /about/policy 路由。
pages/about/index.js 代表的是 /about 路由。
pages/about.js 代表的是 /about 路由。
页面导航需要借助 next/link 组件。
组件 接收字符串,也可以是 URL 对象,而且它会自动格式化生成 URL 字符串。
使用方法如下:
import Link from 'next/link'
...
About Page
...
此时如果想给路由传参数,可以使用 query string ,也可以使用 URL 对象:
About Page
或
// 将生成 URL 字符串/about?title=hello
here
当取参数的时候,可以使用框架提供的 withRouter 方法,参数封装在 query 对象中:
import { withRouter } from 'next/router'
const Page = withRouter(props => (
{props.router.query.title}
))
export default Page
如果希望浏览器地址栏不显示 query string,可以使用as属性:
{props.title}
此时,浏览器的地址栏将显示 localhost:3000/p/123
替换路由
给 标签添加 replace
不会产生新的路由。
here
注意: 的默认行为会滚动到页面顶部。当有 hash 定义时(#),页面将会滚动到对应的 id 上,就像 标签一样。为了预防滚动到顶部,可以给 加 scroll={false} 属性
命令式
除了页面导航,也可以使用框架内置的 next/router 实现客户端路由切换。
import Router from 'next/router'
const handler = () =>
Router.push({
pathname: '/about',
query: { name: 'Zeit' }
})
...
Click here to read more
...
可通过 push 或 replace 进行跳转,参数是 URL 对象(与 组件的 URL 对象一样)。
支持在页面跳转之前进行拦截,只需要监听popstate
:
import Router from 'next/router'
Router.beforePopState(({ url, as, options }) => {
// 只有 / 和 /other 可以正常返回
if (as !== "/" || as !== "/other") {
window.location.href = as
return false
}
return true
});
beforePopState 中返回 false,Router 将不会执行 popstate 事件。
Router 对象的 API:
- route 当前路由的 String 类型
- pathname 不包含查询内容的当前路径,为 String 类型
- query 查询内容,被解析成 Object 类型, 默认为 {}
- asPath 展现在浏览器上的实际路径,包含查询内容,为 String 类型
- push(url, as=url) 页面渲染第一个参数 url 的页面,浏览器栏显示的是第二个参数 url
- replace(url, as=url) 页面渲染第一个参数 url 的页面,浏览器栏显示的是第二个参数 url
- beforePopState(cb=function) 在路由器处理事件之前拦截
路由事件
如果业务复杂,需要监听路由,可以监听下面的事件:
routeChangeStart(url) 路由开始切换时触发
routeChangeComplete(url) 完成路由切换时触发
routeChangeError(err, url) 路由切换报错时触发
beforeHistoryChange(url) 浏览器 history 模式开始切换时触发
hashChangeStart(url) 开始切换 hash 值但是没有切换页面路由时触发
hashChangeComplete(url) 完成切换 hash 值但是没有切换页面路由时触发
如何监听和取消呢?
在路由切换时监听事件:
const handleRouteChange = url => {
console.log('App is changing to: ', url)
}
Router.events.on('routeChangeStart', handleRouteChange)
取消监听事件:
Router.events.off('routeChangeStart', handleRouteChange)
路由取消事件:
Router.events.on('routeChangeError', (err, url) => {
if (err.cancelled) {
console.log(`Route to ${url} was cancelled!`)
}
})
代码分割
import cowsay from 'cowsay-browser'
...
{cowsay.say({ text: 'hi there!' })}
...
页面只会加载通过 import 导入的和用到的代码。即:不会加载不需要的代码。
如想实现组件懒加载,可以通过框架提供的 next/dynamic 工具函数。
支持 SSR
import dynamic from 'next/dynamic'
const DynamicComponent = dynamic(import('../components/hello'))
...
HOME PAGE is here!
...
不支持 SSR
import dynamic from 'next/dynamic'
const DynamicComponentWithNoSSR = dynamic(import('../components/hello3'), {
ssr: false
})
...
HOME PAGE is here!
...
同时加载多个模块
import dynamic from 'next/dynamic'
const Hello = dynamic({
modules: () => {
const components = {
Hello1: import('../components/hello1'),
Hello2: import('../components/hello2')
}
return components
},
render: (props, { Hello1, Hello2 }) =>
{props.title}
})
export default () =>
CSS
支持嵌入样式
Hello world
scoped!
内嵌样式
hello world
使用 CSS / Sass / Less / Stylus 样式文件
需要配置默认文件 next.config.js
静态资源
静态资源默认是在 public
文件夹下,代码可直接引用。
也可以将静态资源放在 static 文件夹下,代码引用时可通过 /static/ 来引入相关的静态资源。
注意:不要自定义静态文件夹的名字,只能叫
static
,因为只有这个名字
Next.js 才会把它当作静态资源。
生成
import Head from 'next/head'
...
My page title
Hello world!
...
注意:只有后面的 head 会最终执行。
预加载页面
Next.js 的预加载功能只预加载 JS 代码。当页面渲染时需要等待数据请求。
添加 prefetch 属性
import Link from 'next/link'
...
...
命令式
import React from 'react'
import { withRouter } from 'next/router'
class MyLink extends React.Component {
componentDidMount() {
const { router } = this.props
// 预加载页面
router.prefetch('/dynamic')
}
render() {
const { router } = this.props
return (
)
}
}
export default withRouter(MyLink)
总结
Next.js 是一个很好用的开发框架,用最少的配置就可以正常开发,降低了开发人员业务外的繁琐工作,因其内置的特性,使得开发人员可以快速开发。