Next.js中文说明
Next.js 是一个轻量级的 React 服务端渲染应用框架。
可访问 nextjs.org/learn 开始学习 Next.js.
怎么使用
安装
在项目文件夹中运行:
1 | npm install --save next react react-dom |
将下面脚本添加到 package.json 中:
1 | { |
下面, 文件系统是主要的 API. 每个.js
文件将变成一个路由,自动处理和渲染。
新建 ./pages/index.js
到你的项目中:
1 | export default () => <div>Welcome to next.js!</div>; |
运行 npm run dev
命令并打开 http://localhost:3000
。 要使用其他端口,你可以运行 npm run dev -- -p <your port here>
.
到目前为止,我们做到:
- 自动打包编译 (使用 webpack 和 babel)
- 热加载
- 以
./pages
作为服务的渲染和索引 - 静态文件服务.
./static/
映射到/static/
(可以 创建一个静态目录 在你的项目中)
这里有个简单的案例,可以下载看看 sample app - nextgram
代码自动分割
每个页面只会导入import
中绑定以及被用到的代码. 这意味着页面不会加载不必要的代码
1 | import cowsay from "cowsay-browser"; |
CSS
支持嵌入样式
案例
我们绑定 styled-jsx 来生成独立作用域的 CSS. 目标是支持 “shadow CSS”,但是 不支持独立模块作用域的 JS.
1 | export default () => ( |
想查看更多案例可以点击 styled-jsx documentation.
内嵌样式
有些情况可以使用 CSS 内嵌 JS 写法。如下所示:
1 | export default () => <p style={{ color: "red" }}>hi there</p>; |
更复杂的内嵌样式解决方案,特别是服务端渲染时的样式更改。我们可以通过包裹自定义 Document,来添加样式,案例如下:custom <Document>
使用 CSS / Sass / Less / Stylus files
支持用.css
, .scss
, .less
or .styl
,需要配置默认文件 next.config.js,具体可查看下面链接
静态文件服务(如图像)
在根目录下新建文件夹叫static
。代码可以通过/static/
来引入相关的静态资源。
1 | export default () => <img src="/static/my-image.png" alt="my image" />; |
注意:不要自定义静态文件夹的名字,只能叫static
,因为只有这个名字 Next.js 才会把它当作静态资源。
生成<head>
<head>
Examples
我们设置一个内置组件来装载<head>
到页面中。
1 | import Head from "next/head"; |
我们定义key
属性来避免重复的<head>
标签,保证<head>
只渲染一次,如下所示:
1 | import Head from "next/head"; |
只有第二个<meta name="viewport" />
才被渲染。
注意:在卸载组件时,<head>
的内容将被清除。请确保每个页面都在其<head>
定义了所需要的内容,而不是假设其他页面已经加过了
获取数据以及组件生命周期
Examples
当你需要状态,生命周期钩子或初始数据填充时,你可以导出React.Component
(而不是上面的无状态函数),如下所示:
1 | import React from "react"; |
请注意,当页面渲染时加载数据,我们使用了一个异步静态方法getInitialProps
。它能异步获取 JS 普通对象,并绑定在props
上。
当服务渲染时,getInitialProps
将会把数据序列化,就像JSON.stringify
。所以确保getInitialProps
返回的是一个普通 JS 对象,而不是Date
, Map
或 Set
类型。
当页面初次加载时,getInitialProps
只会在服务端执行一次。getInitialProps
只有在路由切换的时候(如Link
组件跳转或路由自定义跳转)时,客户端的才会被执行。
当页面初始化加载时,getInitialProps
仅在服务端上执行。只有当路由跳转(Link
组件跳转或 API 方法跳转)时,客户端才会执行getInitialProps
。
注意:getInitialProps
将不能在子组件中使用。只能在pages
页面中使用。
只有服务端用到的模块放在
getInitialProps
里,请确保正确的导入了它们,可参考import them properly。
否则会拖慢你的应用速度。
你也可以给无状态组件定义getInitialProps
:
1 | const Page = ({ stars }) => <div>Next stars: {stars}</div>; |
getInitialProps
入参对象的属性如下:
pathname
- URL 的 path 部分query
- URL 的 query 部分,并被解析成对象asPath
- 显示在浏览器中的实际路径(包含查询部分),为String
类型req
- HTTP 请求对象 (仅限服务器端)res
- HTTP 返回对象 (仅限服务器端)jsonPageRes
- 获取响应对象(仅限客户端)err
- 渲染过程中的任何错误
路由
Next.js不会随应用程序中每个可能的路由一起发布路由清单,因此当前页面不知道客户端上的任何其他页面。出于可扩展性考虑,所有后续路由都会惰性加载。
<Link>
用法
Examples
可以用 <Link>
组件实现客户端的路由切换。
基本例子
参考下面的两个页面:
1 | // pages/index.js |
1 | // pages/about.js |
自定义路由 (使用URL中的props)
<Link>
组件有两个主要属性:
href
:pages
目录内的路径+查询字符串.as
: 将在浏览器URL栏中呈现的路径.
例子:
假设你有个这样的路由
/post/:slug
.你可以创建文件
pages/post.js
1 | class Post extends React.Component { |
- 将路由添加到
express
(或者其他服务端) 的server.js
文件 (这仅适用于SSR). 这将解析/post/:slug
到pages/post.js
并在getInitialProps中提供slug
作为查询的一部分。
1 | server.get('/post/:slug', (req, res) => { |
- 对于客户端路由,使用
next/link
:1
<Link href="/post?slug=something" as="/post/something">
注意:可以使用<Link prefetch>
使链接和预加载在后台同时进行,来达到页面的最佳性能。
客户端路由行为与浏览器很相似:
- 获取组件
- 如果组件定义了
getInitialProps
,则获取数据。如果有错误情况将会渲染_error.js
。 - 1 和 2 都完成了,
pushState
执行,新组件被渲染。
如果需要注入pathname
, query
或 asPath
到你组件中,你可以使用withRouter。
URL 对象
Examples
组件<Link>
接收 URL 对象,而且它会自动格式化生成 URL 字符串
1 | // pages/index.js |
将生成 URL 字符串/about?name=Zeit
,你可以使用任何在Node.js URL module documentation定义过的属性。
替换路由
<Link>
组件默认将新 url 推入路由栈中。你可以使用replace
属性来防止添加新输入。
1 | // pages/index.js |
组件支持点击事件 onClick
<Link>
支持每个组件所支持的onClick
事件。如果你不提供<a>
标签,只会处理onClick
事件而href
将不起作用。
1 | // pages/index.js |
暴露 href
给子元素
如子元素是一个没有 href 属性的<a>
标签,我们将会指定它以免用户重复操作。然而有些时候,我们需要里面有<a>
标签,但是Link
组件不会被识别成超链接,结果不能将href
传递给子元素。在这种场景下,你可以定义一个Link
组件中的布尔属性passHref
,强制将href
传递给子元素。
注意: 使用a
之外的标签而且没有通过passHref
的链接可能会使导航看上去正确,但是当搜索引擎爬行检测时,将不会识别成链接(由于缺乏 href 属性),这会对你网站的 SEO 产生负面影响。
1 | import Link from "next/link"; |
禁止滚动到页面顶部
<Link>
的默认行为就是滚到页面顶部。当有 hash 定义时(#),页面将会滚动到对应的 id 上,就像<a>
标签一样。为了预防滚动到顶部,可以给<Link>
加scroll={false}
属性:
1 | <Link scroll={false} href="/?counter=10"><a>Disables scrolling</a></Link> |
命令式
你也可以用next/router
实现客户端路由切换
1 | import Router from "next/router"; |
拦截器 popstate
有些情况(比如使用custom router),你可能想监听popstate
,在路由跳转前做一些动作。
比如,你可以操作 request 或强制 SSR 刷新
1 | import Router from "next/router"; |
如果你在beforePopState
中返回 false,Router
将不会执行popstate
事件。
例如Disabling File-System Routing。
以上Router
对象的 API 如下:
route
- 当前路由,为String
类型pathname
- 不包含查询内容的当前路径,为String
类型query
- 查询内容,被解析成Object
类型. 默认为{}
asPath
- 展现在浏览器上的实际路径,包含查询内容,为String
类型push(url, as=url)
- 用给定的url调用pushState
replace(url, as=url)
- 用给定的url调用replaceState
beforePopState(cb=function)
- 在路由器处理事件之前拦截.
push
和 replace
函数的第二个参数as
,是为了装饰 URL 作用。如果你在服务器端设置了自定义路由将会起作用。
URL 对象用法
push
或 replace
可接收的 URL 对象(<Link>
组件的 URL 对象一样)来生成 URL。
1 | import Router from "next/router"; |
也可以像<Link>
组件一样添加额外的参数。
路由事件
你可以监听路由相关事件。
下面是支持的事件列表:
routeChangeStart(url)
- 路由开始切换时触发routeChangeComplete(url)
- 完成路由切换时触发routeChangeError(err, url)
- 路由切换报错时触发beforeHistoryChange(url)
- 浏览器 history 模式开始切换时触发hashChangeStart(url)
- 开始切换 hash 值但是没有切换页面路由时触发hashChangeComplete(url)
- 完成切换 hash 值但是没有切换页面路由时触发
这里的
url
是指显示在浏览器中的 url。如果你用了Router.push(url, as)
(或类似的方法),那浏览器中的 url 将会显示 as 的值。
下面是如何正确使用路由事件routeChangeStart
的例子:
1 | const handleRouteChange = url => { |
如果你不想再监听该事件,你可以用off
事件去取消监听:
1 | Router.events.off("routeChangeStart", handleRouteChange); |
如果路由加载被取消(比如快速连续双击链接),routeChangeError
将触发。传递err,并且属性cancelled的值为true。
1 | Router.events.on("routeChangeError", (err, url) => { |
浅层路由
Examples
浅层路由允许你改变 URL 但是不执行getInitialProps
生命周期。你可以加载相同页面的 URL,得到更新后的路由属性pathname
和query
,并不失去 state 状态。
你可以给Router.push
或 Router.replace
方法加shallow: true
参数。如下面的例子所示:
1 | // Current URL is "/" |
现在 URL 更新为/?counter=10
。在组件里查看this.props.router.query
你将会看到更新的 URL。
你可以在componentdidupdate
钩子函数中监听 URL 的变化。
1 | componentDidUpdate(prevProps) { |
注意:
浅层路由只作用于相同 URL 的参数改变,比如我们假定有个其他路由
about
,而你向下面代码样运行:
1 Router.push("/?counter=10", "/about?counter=10", { shallow: true });那么这将会出现新页面,即使我们加了浅层路由,但是它还是会卸载当前页,会加载新的页面并触发新页面的
getInitialProps
。
高阶组件
Examples
如果你想应用里每个组件都处理路由对象,你可以使用withRouter
高阶组件。下面是如何使用它:
1 | import { withRouter } from "next/router"; |
上面路由对象的 API 可以参考next/router
.
预加载页面
⚠️ 只有生产环境才有此功能 ⚠️
Examples
Next.js 有允许你预加载页面的 API。
用 Next.js 服务端渲染你的页面,可以达到所有你应用里所有未来会跳转的路径即时响应,有效的应用 Next.js,可以通过预加载应用程序的功能,最大程度的初始化网站性能。查看更多.
Next.js 的预加载功能只预加载 JS 代码。当页面渲染时,你可能需要等待数据请求。
<Link>
用法
你可以给添加 prefetch
属性,Next.js 将会在后台预加载这些页面。
1 | import Link from "next/link"; |
命令式 prefetch 写法
大多数预加载是通过处理的,但是我们还提供了命令式 API 用于更复杂的场景。
1 | import { withRouter } from "next/router"; |
路由实例只允许在应用程序的客户端。以防服务端渲染发生错误,建议 prefetch 事件写在componentDidMount()
生命周期里。
1 | import React from "react"; |
自定义服务端路由
一般你使用next start
命令来启动 next 服务,你还可以编写代码来自定义路由,如使用路由正则等。
当使用自定义服务文件,如下面例子所示叫 server.js 时,确保你更新了 package.json 中的脚本。
1 | { |
下面这个例子使 /a
路由解析为./pages/b
,以及/b
路由解析为./pages/a
;
1 | // This file doesn't go through babel or webpack transformation. |
next
的 API 如下所示
next(opts: object)
opts 的属性如下:
dev
(boolean
) 判断 Next.js 应用是否在开发环境 - 默认false
dir
(string
) Next 项目路径 - 默认'.'
quiet
(boolean
) 是否隐藏包含服务端消息在内的错误信息 - 默认false
conf
(object
) 与next.config.js
的对象相同 - 默认{}
生产环境的话,可以更改 package.json 里的start
脚本为NODE_ENV=production node server.js
。
禁止文件路由
默认情况,Next
将会把/pages
下的所有文件匹配路由(如/pages/some-file.js
渲染为 site.com/some-file
)
如果你的项目使用自定义路由,那么有可能不同的路由会得到相同的内容,可以优化 SEO 和用户体验。
禁止路由链接到/pages
下的文件,只需设置next.config.js
文件如下所示:
1 | // next.config.js |
注意useFileSystemPublicRoutes
只禁止服务端的文件路由;但是客户端的还是禁止不了。
你如果想配置客户端路由不能跳转文件路由,可以参考Intercepting popstate
。
动态前缀
有时你需要设置动态前缀,可以在请求时设置assetPrefix
改变前缀。
使用方法如下:
1 | const next = require("next"); |
动态导入
Examples
ext.js 支持 JavaScript 的 TC39 提议dynamic import proposal。你可以动态导入 JavaScript 模块(如 React 组件)。
动态导入相当于把代码分成各个块管理。Next.js 服务端动态导入功能,你可以做很多炫酷事情。
下面介绍一些动态导入方式:
1. 基础用法 (也就是SSR)
1 | import dynamic from "next/dynamic"; |
2. 自定义加载组件
1 | import dynamic from "next/dynamic"; |
3. 禁止使用 SSR
1 | import dynamic from "next/dynamic"; |
4. 同时加载多个模块
1 | import dynamic from "next/dynamic"; |
自定义 <App>
组件来初始化页面。你可以重写它来控制页面初始化,如下面的事:
- 当页面变化时保持页面布局
- 当路由变化时保持页面状态
- 使用
componentDidCatch
自定义处理错误 - 注入额外数据到页面里 (如 GraphQL 查询)
重写的话,新建./pages/_app.js
文件,重写 App 模块如下所示:
1 | import App, { Container } from "next/app"; |
自定义 <Document>
- 在服务端呈现
- 初始化服务端时添加文档标记元素
- 通常实现服务端渲染会使用一些 css-in-js 库,如styled-components, glamorous 或 emotion。styled-jsx是 Next.js 自带默认使用的 css-in-js 库
Next.js
会自动定义文档标记,比如,你从来不需要添加<html>
, <body>
等。如果想自定义文档标记,你可以新建./pages/_document.js
,然后扩展Document
类:
1 | // _document is only rendered on the server side and not on the client side |
钩子getInitialProps
接收到的参数ctx
对象都是一样的
- 回调函数
renderPage
是会执行 React 渲染逻辑的函数(同步),这种做法有助于此函数支持一些类似于 Aphrodite 的 renderStatic 等一些服务器端渲染容器。
注意:<Main />
外的 React 组件将不会渲染到浏览器中,所以那添加应用逻辑代码。如果你页面需要公共组件(菜单或工具栏),可以参照上面说的App
组件代替。
自定义 renderPage
🚧 应该注意的是,您应该定制“renderPage”的唯一原因是使用css-in-js库,需要将应用程序包装起来以正确使用服务端渲染。 🚧
- 它将一个选项对象作为参数进行进一步的自定义:
1 | import Document from 'next/document' |
自定义错误处理
404 和 500 错误客户端和服务端都会通过error.js
组件处理。如果你想改写它,则新建_error.js
在文件夹中:
⚠️ 该pages/_error.js
组件仅用于生产。在开发过程中,您会收到调用堆栈错误,以了解错误源自何处。 ⚠️
1 | import React from "react"; |
渲染内置错误页面
如果你想渲染内置错误页面,你可以使用next/error
:
1 | import React from "react"; |
如果你自定义了个错误页面,你可以引入自己的错误页面来代替
next/error
自定义配置
如果你想自定义 Next.js 的高级配置,可以在根目录下新建next.config.js
文件(与pages/
和 package.json
一起)
注意:next.config.js
是一个 Node.js 模块,不是一个 JSON 文件,可以用于 Next 启动服务已经构建阶段,但是不作用于浏览器端。
1 | // next.config.js |
或使用一个函数:
1 | module.exports = (phase, { defaultConfig }) => { |
phase
是配置文件被加载时的当前内容。你可看到所有的 phases 常量:constants
这些常量可以通过next/constants
引入:
1 | const { PHASE_DEVELOPMENT_SERVER } = require("next/constants"); |
设置自定义构建目录
你可以自定义一个构建目录,如新建build
文件夹来代替.next
文件夹成为构建目录。如果没有配置构建目录,构建时将会自动新建.next
文件夹
1 | // next.config.js |
禁止 etag 生成
你可以禁止 etag 生成根据你的缓存策略。如果没有配置,Next 将会生成 etags 到每个页面中。
1 | // next.config.js |
配置 onDemandEntries
Next 暴露一些选项来给你控制服务器部署以及缓存页面:
1 | module.exports = { |
这个只是在开发环境才有的功能。如果你在生成环境中想缓存 SSR 页面,请查看SSR-caching
配置解析路由时的页面文件后缀名
如 typescript 模块@zeit/next-typescript
,需要支持解析后缀名为.ts
的文件。pageExtensions
允许你扩展后缀名来解析各种 pages 下的文件。
1 | // next.config.js |
配置构建 ID
Next.js 使用构建时生成的常量来标识你的应用服务是哪个版本。在每台服务器上运行构建命令时,可能会导致多服务器部署出现问题。为了保持同一个构建 ID,可以配置generateBuildId
函数:
1 | // next.config.js |
自定义 webpack 配置
Examples
可以使用些一些常见的模块
注意: webpack
方法将被执行两次,一次在服务端一次在客户端。你可以用isServer
属性区分客户端和服务端来配置
多配置可以组合在一起,如:
1 | const withTypescript = require("@zeit/next-typescript"); |
为了扩展webpack
使用,可以在next.config.js
定义函数。
1 | // next.config.js is not transformed by Babel. So you can only use javascript features supported by your version of Node.js. |
webpack
的第二个参数是个对象,你可以自定义配置它,对象属性如下所示:
buildId
- 字符串类型,构建的唯一标示dev
-Boolean
型,判断你是否在开发环境下isServer
-Boolean
型,为true
使用在服务端, 为false
使用在客户端.defaultLoaders
- 对象型 ,内部加载器, 你可以如下配置babel
- 对象型,配置babel-loader
.hotSelfAccept
- 对象型,hot-self-accept-loader
配置选项.这个加载器只能用于高阶案例。如@zeit/next-typescript
添加顶层 typescript 页面。
defaultLoaders.babel
使用案例如下:
1 | // Example next.config.js for adding a loader that depends on babel-loader |
自定义 babel 配置
Examples
为了扩展方便我们使用babel
,可以在应用根目录新建.babelrc
文件,该文件可配置。
如果有该文件,我们将会考虑数据源,因此也需要定义 next 项目需要的东西,也就是 next/babel
预设。
这种设计方案将会使你不诧异于我们可以定制 babel 配置。
下面是.babelrc
文件案例:
1 | { |
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
预设:
1 | { |
"preset-env"
模块选项应该保持为 false,否则 webpack 代码分割将被禁用。
暴露配置到服务端和客户端
在应用程序中通常需要提供配置值
Next.js支持2种提供配置的方式:
- 构建时配置
- 运行时配置
构建时配置
构建时配置的工作方式是将提供的值内联到Javascript包中。
你可以在next.config.js
设置env
:
1 | // next.config.js |
这将允许你在代码中使用process.env.customKey
,例如:
1 | // pages/index.js |
运行时配置
⚠️ 请注意,使用此选项时不可用
target: 'serverless'
⚠️ 通常,您希望使用构建时配置来提供配置。原因是运行时配置增加了一个小的rendering/initialization开销。
next/config
模块使你应用运行时可以读取些存储在next.config.js
的配置项。serverRuntimeConfig
属性只在服务器端可用,publicRuntimeConfig
属性在服务端和客户端可用。
1 | // next.config.js |
1 | // pages/index.js |
启动服务选择 hostname
启动开发环境服务可以设置不同的 hostname,你可以在启动命令后面加上--hostname 主机名
或 -H 主机名
。它将会启动一个 TCP 服务器来监听连接所提供的主机。
CDN 支持前缀
建立一个 CDN,你能配置assetPrefix
选项,去配置你的 CDN 源。
1 | const isProd = process.env.NODE_ENV === "production"; |
注意:Next.js 运行时将会自动添加前缀,但是对于/static
是没有效果的,如果你想这些静态资源也能使用 CDN,你需要自己添加前缀。有一个方法可以判断你的环境来加前缀,如 in this example。
项目部署
部署中,你可以先构建打包生成环境代码,再启动服务。因此,构建和启动分为下面两条命令:
1 | next build |
例如,使用now
去部署package.json
配置文件如下:
1 | { |
然后就可以直接运行now
了。
Next.js 也有其他托管解决方案。请查考 wiki 章节‘Deployment’ 。
注意:NODE_ENV
可以通过next
命令配置,如果没有配置,会最大渲染,如果你使用编程式写法的话programmatically,你需要手动设置NODE_ENV=production
。
注意:推荐将.next
或自定义打包文件夹custom dist folder放入.gitignore
或 .npmignore
中。否则,使用files
或 now.files
添加部署白名单,并排除.next
或自定义打包文件夹。
无服务器部署
例子
- now.sh
- anna-artemov.now.sh
- 我们鼓励为本节提供更多示例
无服务器部署通过将应用程序拆分为更小的部分(也称为lambdas)来显着提高可靠性和可伸缩性。在Next.js中,pages
目录中的每个页面都变成了无服务器的lambda。
对于无服务器的人来说,有许多好处。引用的链接在Express的上下文中讨论了其中的一些,但这些原则普遍适用:无服务器允许分布式故障点,无限的可扩展性,并且通过“为您使用的内容付费”的模式来提供难以置信的价格。
要在Next.js中启用无服务器模式,可在Next.config.js
中配置target
值为serverless
:
1 | // next.config.js |
serverless
将每页输出一个lambda。此文件是完全独立的,不需要运行任何依赖项:
pages/index.js
=>.next/serverless/pages/index.js
pages/about.js
=>.next/serverless/pages/about.js
Next.js无服务器功能的签名类似于Node.js HTTP服务器回调:
1 | export function render(req: http.IncomingMessage, res: http.ServerResponse) => void |
- http.IncomingMessage
- http.ServerResponse
void
指的是没有返回值的函数,它等同于JavaScriptundefined
。调用该函数将完成请求。
使用无服务配置, 你可以讲Next.js部署到ZEIT Now 并提供所有的好处和易于控制; custom routes 缓存头. 要了解更多信息,请参阅 ZEIT Guide for Deploying Next.js with Now
降级部署
Next.js为无服务器部署提供低级API,因为托管平台具有不同的功能签名。通常,您需要使用兼容性层包装Next.js无服务器构建的输出。
例如,如果平台支持Node.jshttp.Server
类:
1 | const http = require('http') |
有关特定平台示例,请参阅the examples section above.
摘要
- 用于实现无服务器部署的Low-level API
pages
目录中的每个页面都成为无服务器功能(lambda)- 创建最小的无服务器功能 (50Kb base zip size)
- 针对功能的快速cold start 进行了优化
- 无服务器函数有0个依赖项 (依赖项包含在函数包中)
- 使用Node.js中的http.IncomingMessage和http.ServerResponse
- 选择使用
target: 'serverless'
innext.config.js
- 在执行函数时不要加载
next.config.js
,请注意这意味着publicRuntimeConfig
/serverRuntimeConfig
不支持。
浏览器支持
Next.js 支持 IE11 和所有的现代浏览器使用了@babel/preset-env
。为了支持 IE11,Next.js 需要全局添加Promise
的 polyfill。有时你的代码或引入的其他 NPM 包的部分功能现代浏览器不支持,则需要用 polyfills 去实现。
ployflls 实现案例为polyfills。
导出静态页面
Examples
next export
可以输出一个 Next.js 应用作为静态资源应用而不依靠 Node.js 服务。
这个输出的应用几乎支持 Next.js 的所有功能,包括动态路由,预获取,预加载以及动态导入。
next export
将把所有有可能渲染出的 HTML 都生成。这是基于映射对象的pathname
关键字关联到页面对象。这个映射叫做exportPathMap
。
页面对象有 2 个属性:
page
- 字符串类型,页面生成目录query
- 对象类型,当预渲染时,query
对象将会传入页面的生命周期getInitialProps
中。默认为{}
。
使用
通常开发 Next.js 应用你将会运行:
1 | next build |
next export
命令默认不需要任何配置,将会自动生成默认exportPathMap
生成pages
目录下的路由你页面。
如果你想动态配置路由,可以在next.config.js
中添加异步函数exportPathMap
。
1 | // next.config.js |
注意:如果 path 的结尾是目录名,则将导出
/dir-name/index.html
,但是如果结尾有扩展名,将会导出对应的文件,如上/readme.md
。如果你使用.html
以外的扩展名解析文件时,你需要设置 header 的Content-Type
头为”text/html”.
输入下面命令:
1 | next build |
你可以在package.json
添加一个 NPM 脚本,如下所示:
1 | { |
接着只用执行一次下面命令:
1 | npm run export |
然后你将会有一个静态页面应用在out
目录下。
你也可以自定义输出目录。可以运行
next export -h
命令查看帮助。
现在你可以部署out
目录到任意静态资源服务器上。注意如果部署 GitHub Pages 需要加个额外的步骤,文档如下
例如,访问out
目录并用下面命令部署应用ZEIT Now.
1 | now |
复制自定义文件
如果您必须复制robots.txt等自定义文件或生成sitemap.xml,您可以在其中执行此操作exportPathMap
。 exportPathMap
获取一些上下文参数来帮助您创建/复制文件:
dev
-true
表示在开发环境下使用exportPathMap
.false
表示运行于next export
. 在开发中,“exportpathmap”用于定义路由,不需要复制文件等行为。dir
- 项目目录的绝对路径outDir
- 指向out
目录的绝对路径(可配置为-o
或--outdir
)。当dev
为true
时,outdir
的值将为null
。distDir
-.next
目录的绝对路径(可使用distDir
配置键配置)buildId
- 导出正在运行的buildId
1 | // next.config.js |
限制
使用next export
,我们创建了个静态 HTML 应用。构建时将会运行页面里生命周期getInitialProps
函数。
req
和res
只在服务端可用,不能通过getInitialProps
。
所以你不能预构建 HTML 文件时动态渲染 HTML 页面。如果你想动态渲染可以运行
next start
或其他自定义服务端 API。
多 zone
Examples
一个 zone 时一个单独的 Next.js 应用。如果你有很多 zone,你可以合并成一个应用。
例如,你如下有两个 zone:
- https://docs.my-app.com 服务于路由
/docs/**
- https://ui.my-app.com 服务于所有页面
有多 zone 应用技术支持,你可以将几个应用合并到一个,而且可以自定义 URL 路径,使你能同时单独开发各个应用。
与 microservices 观念类似, 只是应用于前端应用.
怎么定义一个 zone
zone 没有单独的 API 文档。你需要做下面事即可:
- 确保你的应用里只有需要的页面 (例如, https://ui.my-app.com 不包含
/docs/**
) - 确保你的应用有个前缀assetPrefix。(你也可以定义动态前缀dynamically)
怎么合并他们
你能使用 HTTP 代理合并 zone
你能使用代理micro proxy来作为你的本地代理服务。它允许你定义路由规则如下:
1 | { |
生产环境部署,如果你使用了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 还提供两个内置特性:
- 路由与懒加载组件:
<Link>
(通过引入next/link
) - 修改
<head>
的组件:<Head>
(通过引入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 或修改历史记录 <Link />
组件用于包装如(<a/>
)标签的元素容器,来执行客户端转换。
我们使用了些有趣的场景来测试路由的灵活性,例如,可查看nextgram。
怎么获取数据?
这由你决定。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