Next.js 是一个轻量级的 React 服务端渲染应用框架。javascript
Next.js中文站点 http://nextjs.frontendx.cn css
Next.js中文站Github https://github.com/raoenhui/next-site-cn html
当前翻译版本为 7.0.0-canary.8。前端
怎么使用java
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
安装它:
npm install --save next react react-dom
将下面脚本添加到 package.json 中:
{ "scripts": { "dev": "next", "build": "next build", "start": "next start" } }
下面, 文件系统是主要的 API. 每一个.js
文件将变成一个路由,自动处理和渲染。
新建 ./pages/index.js
到你的项目中:
export default () => <div>Welcome to next.js!</div>
运行 npm run dev
命令并打开 http://localhost:3000
。 若是你想使用其余端口,可运行 npm run dev -- -p <设置端口号>
.
目前为止咱们能够了解到:
./pages
做为服务端的渲染和索引./static/
is mapped to /static/
(given you create a ./static/
directory inside your project)./static/
映射到 /static/
(能够 建立一个静态目录 在你的项目中)这里有个简单的案例,能够下载看看 sample app - nextgram
每一个页面只会导入import
中绑定以及被用到的代码. 也就是说并不会加载不须要的代码!
import cowsay from 'cowsay-browser' export default () => <pre> {cowsay.say({ text: 'hi there!' })} </pre>
<p><details>
<summary markdown="span">Examples</summary>
<ul><li>Basic css</li></ul>
</details></p>
咱们绑定 styled-jsx 来生成独立做用域的 CSS. 目标是支持 "shadow CSS",可是 不支持独立模块做用域的 JS.
export default () => <div> Hello world <p>scoped!</p> <style jsx>{` p { color: blue; } div { background: red; } @media (max-width: 600px) { div { background: blue; } } `}</style> <style global jsx>{` body { background: black; } `}</style> </div>
想查看更多案例能够点击 styled-jsx documentation查看.
<p><details>
<summary markdown="span">
<b>Examples</b> </summary>
<ul><li>Styled components</li><li>Styletron</li><li>Glamor</li><li>Glamorous</li><li>Cxs</li><li>Aphrodite</li><li>Fela</li></ul>
</details></p>
有些状况可使用 CSS 内嵌 JS 写法。以下所示:
export default () => <p style={{ color: 'red' }}>hi there</p>
更复杂的内嵌样式解决方案,特别是服务端渲染的时样式更改。咱们能够经过包裹自定义 Document,来添加样式,案例以下:custom <Document>
支持用.css
, .scss
, .less
or .styl
,须要配置默认文件 next.config.js,具体可查看下面连接
在根目录下新建文件夹叫static
。代码能够经过/static/
来引入相关的静态资源。
export default () => <img src="/static/my-image.png" alt="my image" />
_注意:不要自定义静态文件夹的名字,只能叫static
,由于只有这个名字 Next.js 才会把它看成静态资源。
<head>
<p><details>
<summary markdown="span">Examples</summary>
<ul>
<li><a href="https://github.com/zeit/next.js/tree/7.0.0-canary.11/examples/head-elements">Head elements</a></li> <li><a href="https://github.com/zeit/next.js/tree/7.0.0-canary.11/examples/layout-component">Layout component</a></li>
</ul>
</details></p>
咱们设置一个内置组件来装载<head>
到页面中。
import Head from 'next/head' export default () => <div> <Head> <title>My page title</title> <meta name="viewport" content="initial-scale=1.0, width=device-width" /> </Head> <p>Hello world!</p> </div>
咱们定义key
属性来避免重复的<head>
标签,保证<head>
只渲染一次,以下所示:
import Head from 'next/head' export default () => ( <div> <Head> <title>My page title</title> <meta name="viewport" content="initial-scale=1.0, width=device-width" key="viewport" /> </Head> <Head> <meta name="viewport" content="initial-scale=1.2, width=device-width" key="viewport" /> </Head> <p>Hello world!</p> </div> )
只有第二个<meta name="viewport" />
才被渲染。
注意:在卸载组件时,<head>
的内容将被清除。请确保每一个页面都在其<head>
定义了所须要的内容,而不是假设其余页面已经加过了
<p><details>
<summary markdown="span">Examples</summary>
<ul><li>Data fetch</li></ul>
</details></p>
若是你须要一个有状态、生命周期或有初始数据的 React 组件(而不是上面的无状态函数),以下所示:
import React from 'react' export default class extends React.Component { static async getInitialProps({ req }) { const userAgent = req ? req.headers['user-agent'] : navigator.userAgent return { userAgent } } render() { return ( <div> Hello World {this.props.userAgent} </div> ) } }
相信你注意到,当页面渲染时加载数据,咱们使用了一个异步方法getInitialProps
。它能异步获取 JS 普通对象,并绑定在props
上
当服务渲染时,getInitialProps
将会把数据序列化,就像JSON.stringify
。因此确保getInitialProps
返回的是一个普通 JS 对象,而不是Date
, Map
或 Set
类型。
当页面初次加载时,getInitialProps
只会在服务端执行一次。getInitialProps
只有在路由切换的时候(如Link
组件跳转或路由自定义跳转)时,客户端的才会被执行。
当页面初始化加载时,getInitialProps
只会加载在服务端。只有当路由跳转(Link
组件跳转或 API 方法跳转)时,客户端才会执行getInitialProps
。
注意:getInitialProps
将不能使用在子组件中。只能使用在pages
页面中。
<br/>
只有服务端用到的模块放在getInitialProps
里,请确保正确的导入了它们,可参考 import them properly。
不然会拖慢你的应用速度。
<br/>
你也能够给无状态组件定义getInitialProps
:
const Page = ({ stars }) => <div> Next stars: {stars} </div> Page.getInitialProps = async ({ req }) => { const res = await fetch('https://api.github.com/repos/zeit/next.js') const json = await res.json() return { stars: json.stargazers_count } } export default Page
getInitialProps
入参对象的属性以下:
pathname
- URL 的 path 部分query
- URL 的 query 部分,并被解析成对象asPath
- 显示在浏览器中的实际路径(包含查询部分),为String
类型req
- HTTP 请求对象 (只有服务器端有)res
- HTTP 返回对象 (只有服务器端有)jsonPageRes
- 获取数据响应对象 (只有客户端有)err
- 渲染过程当中的任何错误<Link>
用法<p><details>
<summary markdown="span">Examples</summary>
<ul>
<li><a href="https://github.com/zeit/next.js/tree/7.0.0-canary.11/examples/hello-world">Hello World</a></li>
</ul>
</details></p>
能够用 <Link>
组件实现客户端的路由切换。
// pages/index.js import Link from 'next/link' export default () => <div> Click{' '} <Link href="/about"> <a>here</a> </Link>{' '} to read more </div>
// pages/about.js export default () => <p>Welcome to About!</p>
注意:可使用<Link prefetch>
使连接和预加载在后台同时进行,来达到页面的最佳性能。
客户端路由行为与浏览器很类似:
getInitialProps
,数据获取了。若是有错误状况将会渲染 _error.js
。pushState
执行,新组件被渲染。若是须要注入pathname
, query
或 asPath
到你组件中,你可使用withRouter。
<p><details>
<summary markdown="span">Examples</summary>
<ul>
<li><a href="https://github.com/zeit/next.js/tree/7.0.0-canary.11/examples/with-url-object-routing">With URL Object Routing</a></li>
</ul>
</details></p>
组件<Link>
接收 URL 对象,并且它会自动格式化生成 URL 字符串
// pages/index.js import Link from 'next/link' export default () => <div> Click{' '} <Link href={{ pathname: '/about', query: { name: 'Zeit' }}}> <a>here</a> </Link>{' '} to read more </div>
将生成 URL 字符串/about?name=Zeit
,你可使用任何在Node.js URL module documentation定义过的属性。
<Link>
组件默认将新 url 推入路由栈中。你可使用replace
属性来防止添加新输入。
// pages/index.js import Link from 'next/link' export default () => <div> Click{' '} <Link href="/about" replace> <a>here</a> </Link>{' '} to read more </div>
onClick
<Link>
支持每一个组件所支持的onClick
事件。若是你不提供<a>
标签,只会处理onClick
事件而href
将不起做用。
// pages/index.js import Link from 'next/link' export default () => <div> Click{' '} <Link href="/about"> <img src="/static/image.png" alt="image" /> </Link> </div>
href
给子元素如子元素是一个没有 href 属性的<a>
标签,咱们将会指定它以避免用户重复操做。然而有些时候,咱们须要里面有<a>
标签,可是Link
组件不会被识别成超连接,结果不能将href
传递给子元素。在这种场景下,你能够定义一个Link
组件中的布尔属性passHref
,强制将href
传递给子元素。
注意: 使用a
以外的标签并且没有经过passHref
的连接可能会使导航看上去正确,可是当搜索引擎爬行检测时,将不会识别成连接(因为缺少 href 属性),这会对你网站的 SEO 产生负面影响。
import Link from 'next/link' import Unexpected_A from 'third-library' export default ({ href, name }) => <Link href={href} passHref> <Unexpected_A> {name} </Unexpected_A> </Link>
<Link>
的默认行为就是滚到页面顶部。当有 hash 定义时(#),页面将会滚动到对应的 id 上,就像<a>
标签同样。为了预防滚动到顶部,能够给<Link>
加scroll={false}
属性:
<Link scroll={false} href="/?counter=10"><a>Disables scrolling</a></Link> <Link href="/?counter=10"><a>Changes with scrolling to top</a></Link>
<p><details>
<summary markdown="span">Examples</summary>
<ul>
<li><a href="https://github.com/zeit/next.js/tree/7.0.0-canary.11/examples/using-router">Basic routing</a></li> <li><a href="https://github.com/zeit/next.js/tree/7.0.0-canary.11/examples/with-loading">With a page loading indicator</a></li>
</ul>
</details></p>
你也能够用next/router
实现客户端路由切换
import Router from 'next/router' export default () => <div> Click <span onClick={() => Router.push('/about')}>here</span> to read more </div>
popstate
有些状况(好比使用custom router),你可能想监听popstate
,在路由跳转前作一些动做。
好比,你能够操做 request 或强制 SSR 刷新
import Router from 'next/router' Router.beforePopState(({ url, as, options }) => { // I only want to allow these two routes! if (as !== "/" || as !== "/other") { // Have SSR render bad routes as a 404. window.location.href = as return false } return true });
若是你在beforePopState
中返回 false,Router
将不会执行popstate
事件。
例如Disabling File-System Routing。
以上Router
对象的 API 以下:
route
- 当前路由的String
类型pathname
- 不包含查询内容的当前路径,为String
类型query
- 查询内容,被解析成Object
类型. 默认为{}
asPath
- 展示在浏览器上的实际路径,包含查询内容,为String
类型push(url, as=url)
- 页面渲染第一个参数 url 的页面,浏览器栏显示的是第二个参数 urlreplace(url, as=url)
- performs a replaceState
call with the given urlbeforePopState(cb=function)
- 在路由器处理事件以前拦截.push
和 replace
函数的第二个参数as
,是为了装饰 URL 做用。若是你在服务器端设置了自定义路由将会起做用。
push
或 replace
可接收的 URL 对象(<Link>
组件的 URL 对象同样)来生成 URL。
import Router from 'next/router' const handler = () => Router.push({ pathname: '/about', query: { name: 'Zeit' } }) export default () => <div> Click <span onClick={handler}>here</span> to read more </div>
也能够像<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
的例子:
const handleRouteChange = url => { console.log('App is changing to: ', url) } Router.events.on('routeChangeStart', handleRouteChange)
若是你不想长期监听该事件,你能够用off
事件去取消监听:
Router.events.off('routeChangeStart', handleRouteChange)
若是路由加载被取消(好比快速连续双击连接)
Router.events.on('routeChangeError', (err, url) => { if (err.cancelled) { console.log(`Route to ${url} was cancelled!`) } })
<p><details>
<summary markdown="span">Examples</summary>
<ul>
<li><a href="https://github.com/zeit/next.js/tree/7.0.0-canary.11/examples/with-shallow-routing">Shallow Routing</a></li>
</ul>
</details></p>
浅层路由容许你改变 URL 可是不执行getInitialProps
生命周期。你能够加载相同页面的 URL,获得更新后的路由属性pathname
和query
,并不失去 state 状态。
你能够给Router.push
或 Router.replace
方法加shallow: true
参数。以下面的例子所示:
// Current URL is "/" const href = '/?counter=10' const as = href Router.push(href, as, { shallow: true })
如今 URL 更新为/?counter=10
。在组件里查看this.props.router.query
你将会看到更新的 URL。
你能够在componentdidupdate
钩子函数中监听 URL 的变化。
componentDidUpdate(prevProps) { const { pathname, query } = this.props.router // verify props have changed to avoid an infinite loop if (query.id !== prevProps.router.query.id) { // fetch data based on the new query } }
注意:
浅层路由只做用于相同 URL 的参数改变,好比咱们假定有个其余路由
about
,而你向下面代码样运行:Router.push('/?counter=10', '/about?counter=10', { shallow: true })那么这将会出现新页面,即便咱们加了浅层路由,可是它仍是会卸载当前页,会加载新的页面并触发新页面的
getInitialProps
。
<p><details>
<summary markdown="span">Examples</summary>
<ul>
<li><a href="https://github.com/zeit/next.js/tree/7.0.0-canary.11/examples/using-with-router">Using the `withRouter` utility</a></li>
</ul>
</details></p>
若是你想应用里每一个组件都处理路由对象,你可使用withRouter
高阶组件。下面是如何使用它:
import { withRouter } from 'next/router' const ActiveLink = ({ children, router, href }) => { const style = { marginRight: 10, color: router.pathname === href? 'red' : 'black' } const handleClick = (e) => { e.preventDefault() router.push(href) } return ( <a href={href} onClick={handleClick} style={style}> {children} </a> ) } export default withRouter(ActiveLink)
上面路由对象的 API 能够参考next/router
.
⚠️ 只有生产环境才有此功能 ⚠️
<p><details>
<summary markdown="span">Examples</summary>
<ul><li>Prefetching</li></ul>
</details></p>
Next.js 有容许你预加载页面的 API。
用 Next.js 服务端渲染你的页面,能够达到全部你应用里全部将来会跳转的路径即时响应,有效的应用 Next.js,能够经过预加载应用程序的功能,最大程度的初始化网站性能。查看更多.
Next.js 的预加载功能只预加载 JS 代码。当页面渲染时,你可能须要等待数据请求。
<Link>
用法你能够给<Link>添加 prefetch
属性,Next.js 将会在后台预加载这些页面。
import Link from 'next/link' // example header component export default () => <nav> <ul> <li> <Link prefetch href="/"> <a>Home</a> </Link> </li> <li> <Link prefetch href="/about"> <a>About</a> </Link> </li> <li> <Link prefetch href="/contact"> <a>Contact</a> </Link> </li> </ul> </nav>
大多数预加载是经过<Link />处理的,可是咱们还提供了命令式 API 用于更复杂的场景。
import { withRouter } from 'next/router' export default withRouter(({ router }) => <div> <a onClick={() => setTimeout(() => router.push('/dynamic'), 100)}> A route transition will happen after 100ms </a> {// but we can prefetch it! router.prefetch('/dynamic')} </div> )
路由实例只容许在应用程序的客户端。以防服务端渲染发生错误,建议 prefetch 事件写在componentDidMount()
生命周期里。
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 ( <div> <a onClick={() => setTimeout(() => router.push('/dynamic'), 100)}> A route transition will happen after 100ms </a> </div> ) } } export default withRouter(MyLink)
<p><details>
<summary markdown="span">Examples</summary>
<ul>
<li><a href="https://github.com/zeit/next.js/tree/7.0.0-canary.11/examples/custom-server">Basic custom server</a></li> <li><a href="https://github.com/zeit/next.js/tree/7.0.0-canary.11/examples/custom-server-express">Express integration</a></li> <li><a href="https://github.com/zeit/next.js/tree/7.0.0-canary.11/examples/custom-server-hapi">Hapi integration</a></li> <li><a href="https://github.com/zeit/next.js/tree/7.0.0-canary.11/examples/custom-server-koa">Koa integration</a></li> <li><a href="https://github.com/zeit/next.js/tree/7.0.0-canary.11/examples/parameterized-routing">Parameterized routing</a></li> <li><a href="https://github.com/zeit/next.js/tree/7.0.0-canary.11/examples/ssr-caching">SSR caching</a></li>
</ul>
</details></p>
通常你使用next start
命令来启动 next 服务,你还能够编写代码来自定义路由,如使用路由正则等。
当使用自定义服务文件,以下面例子所示叫 server.js 时,确保你更新了 package.json 中的脚本。
{ "scripts": { "dev": "node server.js", "build": "next build", "start": "NODE_ENV=production node server.js" } }
下面这个例子使 /a
路由解析为./pages/b
,以及/b
路由解析为./pages/a
;
// This file doesn't go through babel or webpack transformation. // Make sure the syntax and sources this file requires are compatible with the current node version you are running // See https://github.com/zeit/next.js/issues/1245 for discussions on Universal Webpack or universal Babel const { createServer } = require('http') const { parse } = require('url') const next = require('next') const dev = process.env.NODE_ENV !== 'production' const app = next({ dev }) const handle = app.getRequestHandler() app.prepare().then(() => { createServer((req, res) => { // Be sure to pass `true` as the second argument to `url.parse`. // This tells it to parse the query portion of the URL. const parsedUrl = parse(req.url, true) const { pathname, query } = parsedUrl if (pathname === '/a') { app.render(req, res, '/b', query) } else if (pathname === '/b') { app.render(req, res, '/a', query) } else { handle(req, res, parsedUrl) } }).listen(3000, err => { if (err) throw err console.log('> Ready on http://localhost:3000') }) })
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
文件以下所示:
// next.config.js module.exports = { useFileSystemPublicRoutes: false }
注意useFileSystemPublicRoutes
只禁止服务端的文件路由;可是客户端的仍是禁止不了。
你若是想配置客户端路由不能跳转文件路由,能够参考Intercepting popstate
。
有时你须要设置动态前缀,能够在请求时设置assetPrefix
改变前缀。
使用方法以下:
const next = require('next') const micro = require('micro') const dev = process.env.NODE_ENV !== 'production' const app = next({ dev }) const handleNextRequests = app.getRequestHandler() app.prepare().then(() => { const server = micro((req, res) => { // Add assetPrefix support based on the hostname if (req.headers.host === 'my-app.com') { app.setAssetPrefix('http://cdn.com/myapp') } else { app.setAssetPrefix('') } handleNextRequests(req, res) }) server.listen(port, (err) => { if (err) { throw err } console.log(`> Ready on http://localhost:${port}`) }) })
<p><details>
<summary markdown="span">Examples</summary>
<ul>
<li><a href="https://github.com/zeit/next.js/tree/7.0.0-canary.11/examples/with-dynamic-import">With Dynamic Import</a></li>
</ul>
</details></p>
ext.js 支持 JavaScript 的 TC39 提议dynamic import proposal。你能够动态导入 JavaScript 模块(如 React 组件)。
动态导入至关于把代码分红各个块管理。Next.js 服务端动态导入功能,你能够作不少炫酷事情。
下面介绍一些动态导入方式:
import dynamic from 'next/dynamic' const DynamicComponent = dynamic(import('../components/hello')) export default () => <div> <Header /> <DynamicComponent /> <p>HOME PAGE is here!</p> </div>
import dynamic from 'next/dynamic' const DynamicComponentWithCustomLoading = dynamic( import('../components/hello2'), { loading: () => <p>...</p> } ) export default () => <div> <Header /> <DynamicComponentWithCustomLoading /> <p>HOME PAGE is here!</p> </div>
import dynamic from 'next/dynamic' const DynamicComponentWithNoSSR = dynamic(import('../components/hello3'), { ssr: false }) export default () => <div> <Header /> <DynamicComponentWithNoSSR /> <p>HOME PAGE is here!</p> </div>
import dynamic from 'next/dynamic' const HelloBundle = dynamic({ modules: () => { const components = { Hello1: import('../components/hello1'), Hello2: import('../components/hello2') } return components }, render: (props, { Hello1, Hello2 }) => <div> <h1> {props.title} </h1> <Hello1 /> <Hello2 /> </div> }) export default () => <HelloBundle title="Dynamic Bundle" />
<App>
<p><details>
<summary markdown="span">Examples</summary>
<ul><li>Using _app.js
for layout</li></ul>
<ul><li>Using _app.js
to override componentDidCatch
</li></ul>
</details></p>
组件来初始化页面。你能够重写它来控制页面初始化,以下面的事:
componentDidCatch
自定义处理错误重写的话,新建./pages/_app.js
文件,重写 App 模块以下所示:
import App, {Container} from 'next/app' import React from 'react' export default class MyApp extends App { static async getInitialProps ({ Component, router, ctx }) { let pageProps = {} if (Component.getInitialProps) { pageProps = await Component.getInitialProps(ctx) } return {pageProps} } render () { const {Component, pageProps} = this.props return <Container> <Component {...pageProps} /> </Container> } }
<Document>
<p><details>
<summary markdown="span">Examples</summary>
<ul><li>Styled components custom document</li></ul>
<ul><li>Google AMP</li></ul>
</details></p>
Next.js
会自动定义文档标记,好比,你历来不须要添加<html>
, <body>
等。若是想自定义文档标记,你能够新建./pages/_document.js
,而后扩展Document
类:
// _document is only rendered on the server side and not on the client side // Event handlers like onClick can't be added to this file // ./pages/_document.js import Document, { Head, Main, NextScript } from 'next/document' export default class MyDocument extends Document { static async getInitialProps(ctx) { const initialProps = await Document.getInitialProps(ctx) return { ...initialProps } } render() { return ( <html> <Head> <style>{`body { margin: 0 } /* custom! */`}</style> </Head> <body className="custom_class"> <Main /> <NextScript /> </body> </html> ) } }
钩子getInitialProps
接收到的参数ctx
对象都是同样的
renderPage
是会执行 React 渲染逻辑的函数(同步),这种作法有助于此函数支持一些相似于 Aphrodite 的 renderStatic 等一些服务器端渲染容器。注意:<Main />
外的 React 组件将不会渲染到浏览器中,因此那添加应用逻辑代码。若是你页面须要公共组件(菜单或工具栏),能够参照上面说的App
组件代替。
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 ( <p> {this.props.statusCode ? `An error ${this.props.statusCode} occurred on server` : 'An error occurred on client'} </p> ) } }
若是你想渲染内置错误页面,你可使用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 <Error statusCode={this.props.statusCode} /> } return ( <div> Next stars: {this.props.stars} </div> ) } }
若是你自定义了个错误页面,你能够引入本身的错误页面来代替
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 生成根据你的缓存策略。若是没有配置,Next 将会生成 etags 到每一个页面中。
// next.config.js module.exports = { generateEtags: false }
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'] }
Next.js 使用构建时生成的常量来标识你的应用服务是哪一个版本。在每台服务器上运行构建命令时,可能会致使多服务器部署出现问题。为了保持同一个构建 ID,能够配置generateBuildId
函数:
// next.config.js module.exports = { generateBuildId: async () => { // For example get the latest git commit hash here return 'my-build-id' } }
<p><details>
<summary markdown="span">Examples</summary>
<ul><li>Custom webpack bundle analyzer</li></ul>
</details></p>
可使用些一些常见的模块
注意: 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 } }
<p><details>
<summary markdown="span">Examples</summary>
<ul><li>Custom babel configuration</li></ul>
</details></p>
为了扩展方便咱们使用babel
,能够在应用根目录新建.babelrc
文件,该文件可配置。
若是有该文件,咱们将会考虑数据源,所以也须要定义 next 项目须要的东西,也就是 next/babel
预设。
这种设计方案将会使你不诧异于咱们能够定制 babel 配置。
下面是.babelrc
文件案例:
{ "presets": ["next/babel"], "plugins": [] }
next/babel
预设可处理各类 React 应用所须要的状况。包括:
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 () => <div> <img src={`${publicRuntimeConfig.staticFolder}/logo.png`} alt="logo" /> </div>
启动开发环境服务能够设置不一样的 hostname,你能够在启动命令后面加上--hostname 主机名
或 -H 主机名
。它将会启动一个 TCP 服务器来监听链接所提供的主机。
创建一个 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。
<p><details>
<summary markdown="span">Examples</summary>
<ul><li>Static export</li></ul>
</details></p>
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。
<p><details>
<summary markdown="span">Examples</summary>
<ul><li>With Zones</li></ul>
</details></p>
一个 zone 时一个单独的 Next.js 应用。若是你有不少 zone,你能够合并成一个应用。
例如,你以下有两个 zone:
/docs/**
有多 zone 应用技术支持,你能够将几个应用合并到一个,并且能够自定义 URL 路径,使你能同时单独开发各个应用。
与 microservices 观念相似, 只是应用于前端应用.
zone 没有单独的 API 文档。你须要作下面事便可:
/docs/**
)你能使用 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 页面
<details>
<summary markdown="span">这个产品能够用于生产环境吗?</summary>
<div markdown="span">
https://zeit.co 都是一直用 Next.js 写的。
它的开发体验和终端用户体验都很好,因此咱们决定开源出来给你们共享。
</div>
</details>
<details>
<summary markdown="span">体积多大?</summary>
<div markdown="span">
客户端大小根据应用需求不同大小也不同。
一个最简单 Next 应该用 gzip 压缩后大约65kb
</div>
</details>
<details >
<summary markdown="span">这个像 create-react-app
?</summary>
<div markdown="span">
是或不是.
是,由于它让你的 SSR 开发更简单。
不是,由于它规定了必定的目录结构,使咱们能作如下更高级的事:
此外,Next.js 还提供两个内置特性:
<Link>
(经过引入 next/link
)<head>
的组件: <Head>
(经过引入 next/head
)若是你想写共用组件,能够嵌入 Next.js 应用和 React 应用中,推荐使用create-react-app
。你能够更改import
保持代码清晰。
</div>
</details>
<details>
<summary markdown="span">怎么解决 CSS 嵌入 JS 问题?</summary>
<div markdown="span">
Next.js 自带styled-jsx库支持 CSS 嵌入 JS。并且你能够选择其余嵌入方法到你的项目中,可参考文档as mentioned before。
</div>
</details>
<details>
<summary markdown="span">哪些语法会被转换?怎么转换它们?</summary>
<div markdown="span">
咱们遵循 V8 引擎的,现在 V8 引擎普遍支持 ES6 语法以及async
和await
语法,因此咱们支持转换它们。可是 V8 引擎不支持修饰器语法,因此咱们也不支持转换这语法。
<details>
<summary markdown="span">为何使用新路由?</summary>
<div markdown="span">
Next.js 的特别之处以下所示:
getInitialProps
来阻止路由加载(当服务端渲染或路由懒加载时)所以,咱们能够介绍一个很是简单的路由方法,它由下面两部分组成:
url
对象,来检查 url 或修改历史记录<Link />
组件用于包装如(<a/>
)标签的元素容器,来执行客户端转换。咱们使用了些有趣的场景来测试路由的灵活性,例如,可查看nextgram。
</div>
</details>
<details>
<summary markdown="span">我怎么定义自定义路由?</summary>
<div markdown="span">
咱们经过请求处理来添加任意 URL 与任意组件以前的映射关系。
在客户端,咱们<Link>
组件有个属性as
,能够装饰改变获取到的 URL。
</div>
</details>
<details>
<summary markdown="span">怎么获取数据?</summary>
<div markdown="span">
这由你决定。getInitialProps
是一个异步函数async
(也就是函数将会返回个Promise
)。你能够在任意位置获取数据。
</div>
</details>
<details>
<summary markdown="span">我可使用 GraphQL 吗?</summary>
<div markdown="span">
是的! 这里有个例子Apollo.
</div>
</details>
<details>
<summary markdown="span">我可使用 Redux 吗?</summary>
<div markdown="span">
是的! 这里有个例子
</div>
</details>
<details>
<summary markdown="span">我能够在 Next 应用中使用我喜欢的 Javascript 库或工具包吗?</summary>
<div markdown="span">
从咱们第一次发版就已经提供不少例子,你能够查看这些例子。
</div>
</details>
<details>
<summary markdown="span">什么启发咱们作这个?</summary>
<div markdown="span">
咱们实现的大部分目标都是经过 Guillermo Rauch 的Web 应用的7原则来启发出的。
PHP 的易用性也是个很好的灵感来源,咱们以为 Next.js 能够替代不少须要用 PHP 输出 HTML 的场景。
与 PHP 不一样的是,咱们得利于 ES6 模块系统,每一个文件会输出一个组件或方法,以即可以轻松的导入用于懒加载和测试
咱们研究 React 的服务器渲染时并无花费很大的步骤,由于咱们发现一个相似于 Next.js 的产品,React 做者 Jordan Walke 写的react-page (如今已经废弃)
</div>
</details>
可查看 contributing.md