404和500错误客户端和服务端都会通过error.js组件处理。如果你想改写它,则新建_error.js在文件夹中:
</>复制代码
import React from "react"
export default class Error extends React.Component {
static getInitialProps({ res, err }) {
const statusCode = res ? res.statusCode : err ? err.statusCode : null;
return { statusCode }
}
render() {
return (
{this.props.statusCode
? `An error ${this.props.statusCode} occurred on server`
: "An error occurred on client"}
)
}
}
渲染内置错误页面
如果你想渲染内置错误页面,你可以使用next/error:
</>复制代码
import React from "react"
import Error from "next/error"
import fetch from "isomorphic-unfetch"
export default class Page extends React.Component {
static async getInitialProps() {
const res = await fetch("https://api.github.com/repos/zeit/next.js")
const statusCode = res.statusCode > 200 ? res.statusCode : false
const json = await res.json()
return { statusCode, stars: json.stargazers_count }
}
render() {
if (this.props.statusCode) {
return
}
return (
Next stars: {this.props.stars}
)
}
}
</>复制代码
如果你自定义了个错误页面,你可以引入自己的错误页面来代替next/error
自定义配置
如果你想自定义 Next.js 的高级配置,可以在根目录下新建next.config.js文件(与pages/ 和 package.json一起)
注意:next.config.js是一个 Node.js 模块,不是一个 JSON 文件,可以用于 Next 启动服务已经构建阶段,但是不作用于浏览器端。
</>复制代码
// next.config.js
module.exports = {
/* config options here */
}
或使用一个函数:
</>复制代码
module.exports = (phase, {defaultConfig}) => {
//
// https://github.com/zeit/
return {
/* config options here */
}
}
phase是配置文件被加载时的当前内容。你可看到所有的 phases 常量:constants
这些常量可以通过next/constants引入:
</>复制代码
const {PHASE_DEVELOPMENT_SERVER} = require("next/constants")
module.exports = (phase, {defaultConfig}) => {
if(phase === PHASE_DEVELOPMENT_SERVER) {
return {
/* development only config options here */
}
}
return {
/* config options for all phases except development here */
}
}
设置自定义构建目录
你可以自定义一个构建目录,如新建build文件夹来代替.next 文件夹成为构建目录。如果没有配置构建目录,构建时将会自动新建.next文件夹
</>复制代码
// next.config.js
module.exports = {
distDir: "build"
}
禁止 etag 生成
你可以禁止 etag 生成根据你的缓存策略。如果没有配置,Next 将会生成 etags 到每个页面中。
</>复制代码
// next.config.js
module.exports = {
generateEtags: false
}
配置 onDemandEntries
Next 暴露一些选项来给你控制服务器部署以及缓存页面:
</>复制代码
module.exports = {
onDemandEntries: {
// period (in ms) where the server will keep pages in the buffer
maxInactiveAge: 25 * 1000,
// number of pages that should be kept simultaneously without being disposed
pagesBufferLength: 2,
}
}
这个只是在开发环境才有的功能。如果你在生成环境中想缓存 SSR 页面,请查看SSR-caching
配置页面后缀名解析扩展如 typescript 模块@zeit/next-typescript,需要支持解析后缀名为.ts的文件。pageExtensions 允许你扩展后缀名来解析各种 pages 下的文件。
</>复制代码
// next.config.js
module.exports = {
pageExtensions: ["jsx", "js"]
}
配置构建 ID
Next.js 使用构建时生成的常量来标识你的应用服务是哪个版本。在每台服务器上运行构建命令时,可能会导致多服务器部署出现问题。为了保持同一个构建 ID,可以配置generateBuildId函数:
</>复制代码
// next.config.js
module.exports = {
generateBuildId: async () => {
// For example get the latest git commit hash here
return "my-build-id"
}
}
自定义 webpack 配置
Examples
- Custom webpack bundle analyzer
可以使用些一些常见的模块
@zeit/next-css
@zeit/next-sass
@zeit/next-less
@zeit/next-preact
@zeit/next-typescript
注意: webpack方法将被执行两次,一次在服务端一次在客户端。你可以用isServer属性区分客户端和服务端来配置
多配置可以组合在一起,如:
</>复制代码
const withTypescript = require("@zeit/next-typescript")
const withSass = require("@zeit/next-sass")
module.exports = withTypescript(withSass({
webpack(config, options) {
// Further custom configuration here
return config
}
}))
为了扩展webpack使用,可以在next.config.js定义函数。
</>复制代码
// next.config.js is not transformed by Babel. So you can only use javascript features supported by your version of Node.js.
module.exports = {
webpack: (config, { buildId, dev, isServer, defaultLoaders }) => {
// Perform customizations to webpack config
// Important: return the modified config
return config
},
webpackDevMiddleware: config => {
// Perform customizations to webpack dev middleware config
// Important: return the modified config
return config
}
}
webpack的第二个参数是个对象,你可以自定义配置它,对象属性如下所示:
buildId - 字符串类型,构建的唯一标示
dev - Boolean型,判断你是否在开发环境下
isServer - Boolean 型,为true使用在服务端, 为false使用在客户端.
defaultLoaders - 对象型 ,内部加载器, 你可以如下配置
babel - 对象型,配置babel-loader.
hotSelfAccept - 对象型, hot-self-accept-loader配置选项.这个加载器只能用于高阶案例。如 @zeit/next-typescript添加顶层 typescript 页面。
defaultLoaders.babel使用案例如下:
</>复制代码
// Example next.config.js for adding a loader that depends on babel-loader
// This source was taken from the @zeit/next-mdx plugin source:
// https://github.com/zeit/next-plugins/blob/master/packages/next-mdx
module.exports = {
webpack: (config, {}) => {
config.module.rules.push({
test: /.mdx/,
use: [
options.defaultLoaders.babel,
{
loader: "@mdx-js/loader",
options: pluginOptions.options
}
]
})
return config
}
}
自定义 babel 配置
Examples
- Custom babel configuration
为了扩展方便我们使用babel,可以在应用根目录新建.babelrc文件,该文件可配置。
如果有该文件,我们将会考虑数据源,因此也需要定义 next 项目需要的东西,也就是 next/babel预设。
这种设计方案将会使你不诧异于我们可以定制 babel 配置。
下面是.babelrc文件案例:
</>复制代码
{
"presets": ["next/babel"],
"plugins": []
}
next/babel预设可处理各种 React 应用所需要的情况。包括:
preset-env
preset-react
plugin-proposal-class-properties
plugin-proposal-object-rest-spread
plugin-transform-runtime
styled-jsx
presets / plugins 不允许添加到.babelrc中,然而你可以配置next/babel预设:
</>复制代码
{
"presets": [
["next/babel", {
"preset-env": {},
"transform-runtime": {},
"styled-jsx": {},
"class-properties": {}
}]
],
"plugins": []
}
"preset-env"模块选项应该保持为 false,否则 webpack 代码分割将被禁用。
暴露配置到服务端和客户端next/config模块使你应用运行时可以读取些存储在next.config.js的配置项。serverRuntimeConfig属性只在服务器端可用,publicRuntimeConfig属性在服务端和客户端可用。
</>复制代码
// next.config.js
module.exports = {
serverRuntimeConfig: { // Will only be available on the server side
mySecret: "secret"
},
publicRuntimeConfig: { // Will be available on both server and client
staticFolder: "/static",
mySecret: process.env.MY_SECRET // Pass through env variables
}
}
</>复制代码
// pages/index.js
import getConfig from "next/config"
// Only holds serverRuntimeConfig and publicRuntimeConfig from next.config.js nothing else.
const {serverRuntimeConfig, publicRuntimeConfig} = getConfig()
console.log(serverRuntimeConfig.mySecret) // Will only be available on the server side
console.log(publicRuntimeConfig.staticFolder) // Will be available on both server and client
export default () =>
启动服务选择 hostname
启动开发环境服务可以设置不同的 hostname,你可以在启动命令后面加上--hostname 主机名 或 -H 主机名。它将会启动一个 TCP 服务器来监听连接所提供的主机。
CDN 支持前缀建立一个 CDN,你能配置assetPrefix选项,去配置你的 CDN 源。
</>复制代码
const isProd = process.env.NODE_ENV === "production"
module.exports = {
// You may only need to add assetPrefix in the production.
assetPrefix: isProd ? "https://cdn.mydomain.com" : ""
}
注意:Next.js 运行时将会自动添加前缀,但是对于/static是没有效果的,如果你想这些静态资源也能使用 CDN,你需要自己添加前缀。有一个方法可以判断你的环境来加前缀,如 in this example。
项目部署部署中,你可以先构建打包生成环境代码,再启动服务。因此,构建和启动分为下面两条命令:
</>复制代码
next build
next start
例如,使用now去部署package.json配置文件如下:
</>复制代码
{
"name": "my-app",
"dependencies": {
"next": "latest"
},
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
}
}
然后就可以直接运行now了。
Next.js 也有其他托管解决方案。请查考 wiki 章节"Deployment" 。
注意:NODE_ENV可以通过next命令配置,如果没有配置,会最大渲染,如果你使用编程式写法的话programmatically,你需要手动设置NODE_ENV=production。
注意:推荐将.next或自定义打包文件夹custom dist folder放入.gitignore 或 .npmignore中。否则,使用files 或 now.files
添加部署白名单,并排除.next或自定义打包文件夹。
Next.js 支持 IE11 和所有的现代浏览器使用了@babel/preset-env。为了支持 IE11,Next.js 需要全局添加Promise的 polyfill。有时你的代码或引入的其他 NPM 包的部分功能现代浏览器不支持,则需要用 polyfills 去实现。
ployflls 实现案例为polyfills。
导出静态页面Examples
- Static export
next export可以输出一个 Next.js 应用作为静态资源应用而不依靠 Node.js 服务。
这个输出的应用几乎支持 Next.js 的所有功能,包括动态路由,预获取,预加载以及动态导入。
next export将把所有有可能渲染出的 HTML 都生成。这是基于映射对象的pathname关键字关联到页面对象。这个映射叫做exportPathMap。
页面对象有2个属性:
page - 字符串类型,页面生成目录
query - 对象类型,当预渲染时,query对象将会传入页面的生命周期getInitialProps中。默认为{}。
使用通常开发 Next.js 应用你将会运行:
</>复制代码
next build
next export
next export命令默认不需要任何配置,将会自动生成默认exportPathMap生成pages目录下的路由你页面。
如果你想动态配置路由,可以在next.config.js中添加异步函数exportPathMap。
</>复制代码
// next.config.js
module.exports = {
exportPathMap: async function (defaultPathMap) {
return {
"/": { page: "/" },
"/about": { page: "/about" },
"/readme.md": { page: "/readme" },
"/p/hello-nextjs": { page: "/post", query: { title: "hello-nextjs" } },
"/p/learn-nextjs": { page: "/post", query: { title: "learn-nextjs" } },
"/p/deploy-nextjs": { page: "/post", query: { title: "deploy-nextjs" } }
}
}
}
</>复制代码
注意:如果 path 的结尾是目录名,则将导出/dir-name/index.html,但是如果结尾有扩展名,将会导出对应的文件,如上/readme.md。如果你使用.html以外的扩展名解析文件时,你需要设置 header 的Content-Type头为"text/html".
输入下面命令:
</>复制代码
next build
next export
你可以在package.json添加一个 NPM 脚本,如下所示:
</>复制代码
{
"scripts": {
"build": "next build",
"export": "npm run build && next export"
}
}
接着只用执行一次下面命令:
</>复制代码
npm run export
然后你将会有一个静态页面应用在out 目录下。
</>复制代码
你也可以自定义输出目录。可以运行next export -h命令查看帮助。
现在你可以部署out目录到任意静态资源服务器上。注意如果部署 GitHub Pages 需要加个额外的步骤,文档如下
例如,访问out目录并用下面命令部署应用ZEIT Now.
限制</>复制代码
now
使用next export,我们创建了个静态 HTML 应用。构建时将会运行页面里生命周期getInitialProps 函数。
req和res只在服务端可用,不能通过getInitialProps。
</>复制代码
所以你不能预构建 HTML 文件时动态渲染 HTML 页面。如果你想动态渲染可以运行next start或其他自定义服务端 API。
多 zone
Examples
- With Zones
一个 zone 时一个多带带的 Next.js 应用。如果你有很多 zone,你可以合并成一个应用。
例如,你如下有两个 zone:
https://docs.my-app.com 服务于路由 /docs/**
https://ui.my-app.com 服务于所有页面
有多 zone 应用技术支持,你可以将几个应用合并到一个,而且可以自定义 URL 路径,使你能同时多带带开发各个应用。
怎么定义一个 zone</>复制代码
与 microservices 观念类似, 只是应用于前端应用.
zone 没有多带带的 API 文档。你需要做下面事即可:
确保你的应用里只有需要的页面 (例如, https://ui.my-app.com 不包含 /docs/**)
确保你的应用有个前缀assetPrefix。(你也可以定义动态前缀dynamically)
怎么合并他们你能使用 HTTP 代理合并 zone
你能使用代理micro proxy来作为你的本地代理服务。它允许你定义路由规则如下:
</>复制代码
{
"rules": [
{"pathname": "/docs**", "method":["GET", "POST", "OPTIONS"], "dest": "https://docs.my-app.com"},
{"pathname": "/**", "dest": "https://ui.my-app.com"}
]
}
生产环境部署,如果你使用了ZEIT now,可以它的使用path alias 功能。否则,你可以设置你已使用的代理服务编写上面规则来路由 HTML 页面
技巧设置301重定向
只处理服务器端模块
构建项目 React-Material-UI-Next-Express-Mongoose-Mongodb
构建一个 SaaS 产品 React-Material-UI-Next-MobX-Express-Mongoose-MongoDB-TypeScript
问答这个产品可以用于生产环境吗?
https://zeit.co 都是一直用 Next.js 写的。
它的开发体验和终端用户体验都很好,所以我们决定开源出来给大家共享。
体积多大?
客户端大小根据应用需求不一样大小也不一样。
一个最简单 Next 应该用 gzip 压缩后大约65kb
这个像 create-react-app?
是或不是.
是,因为它让你的 SSR 开发更简单。
不是,因为它规定了一定的目录结构,使我们能做以下更高级的事:
服务端渲染
自动代码分割
此外,Next.js 还提供两个内置特性:
路由与懒加载组件: (通过引入 next/link)
修改的组件: (通过引入 next/head)
如果你想写共用组件,可以嵌入 Next.js 应用和 React 应用中,推荐使用create-react-app。你可以更改import保持代码清晰。
怎么解决 CSS 嵌入 JS 问题?
Next.js 自带styled-jsx库支持 CSS 嵌入 JS。而且你可以选择其他嵌入方法到你的项目中,可参考文档as mentioned before。
哪些语法会被转换?怎么转换它们?
我们遵循 V8 引擎的,如今 V8 引擎广泛支持 ES6 语法以及async和await语法,所以我们支持转换它们。但是 V8 引擎不支持修饰器语法,所以我们也不支持转换这语法。
可以参照这些 以及 这些
为什么使用新路由?
Next.js 的特别之处如下所示:
路由不需要被提前知道
路由总是被懒加载
顶层组件可以定义生命周期getInitialProps来阻止路由加载(当服务端渲染或路由懒加载时)
因此,我们可以介绍一个非常简单的路由方法,它由下面两部分组成:
每个顶层组件都将会收到一个url对象,来检查 url 或修改历史记录
组件用于包装如()标签的元素容器,来执行客户端转换。
我们使用了些有趣的场景来测试路由的灵活性,例如,可查看nextgram。
我怎么定义自定义路由?
我们通过请求处理来添加任意 URL 与任意组件之前的映射关系。
在客户端,我们组件有个属性as,可以装饰改变获取到的 URL。
怎么获取数据?
这由你决定。getInitialProps是一个异步函数async(也就是函数将会返回个Promise)。你可以在任意位置获取数据。
我可以使用 GraphQL 吗?
是的! 这里有个例子Apollo.
我可以使用 Redux 吗?
是的! 这里有个例子
我可以在 Next 应用中使用我喜欢的 Javascript 库或工具包吗?
从我们第一次发版就已经提供很多例子,你可以查看这些例子。
什么启发我们做这个?
我们实现的大部分目标都是通过 Guillermo Rauch 的Web 应用的7原则来启发出的。
PHP 的易用性也是个很好的灵感来源,我们觉得 Next.js 可以替代很多需要用 PHP 输出 HTML 的场景。
与 PHP 不同的是,我们得利于 ES6 模块系统,每个文件会输出一个组件或方法,以便可以轻松的导入用于懒加载和测试
我们研究 React 的服务器渲染时并没有花费很大的步骤,因为我们发现一个类似于 Next.js 的产品,React 作者 Jordan Walke 写的react-page (现在已经废弃)
可查看 contributing.md
作者Arunoda Susiripala (@arunoda) – ZEIT
Tim Neutkens (@timneutkens) – ZEIT
Naoyuki Kanezawa (@nkzawa) – ZEIT
Tony Kovanen (@tonykovanen) – ZEIT
Guillermo Rauch (@rauchg) – ZEIT
Dan Zajdband (@impronunciable) – Knight-Mozilla / Coral Project