Next.js v4.1.4 文档中文翻译

最近想稍稍看下 ReactSSR框架 Next.js,由于不想看二手资料, 因此本身跑到 Github上看,Next.js的文档是英文的,看却是大概也能看得懂, 但有些地方不太肯定,并且英文看着毕竟不太爽你懂得,因此在网上搜了几圈发现好像好像尚未中文翻译,想着长痛不如短痛, 索性一边看一边翻译,本身翻译的东西本身看得也爽,不过毕竟能力有限,有些地方我也不知道该怎么翻译才好,因此翻译得不太通畅, 或者有几句干脆不翻译了。css

so,各位如果以为我哪点翻译得不太准确,或者对于那几句我没翻译的地方有更好的看法,欢迎提出~html

如下是全文翻译的 Next.jsREADME.md文件,版本是 v4.1.4,除了翻译原文以外,还加了一点我的小小看法。node

另外,没太弄明白掘金写文章的md页面内超连接的语法是什么,因此下面的目录超连接没有效果,不过不影响阅读,想要更好的阅读体验能够去个人 github上看,别忘了 star哦~react


screen shot 2016-10-25 at 2 37 27 pm

Next.js是一个用于React应用的极简的服务端渲染框架。webpack

请访问 learnnextjs.com 以获取更多详细内容.git


如何使用

安装

安装方法:github

npm install --save next react react-dom
复制代码

Next.js 4 只支持 React 16.
因为 React 16React 15 的工做方式以及使用方法不尽相同,因此咱们不得不移除了对 React 15 的支持web

在你的 package.json文件中添加以下代码:express

{
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
  }
}
复制代码

接下来,大部分事情都交由文件系统来处理。每一个 .js 文件都变成了一个自动处理和渲染的路由。npm

在项目中新建 ./pages/index.js

export default () => <div>Welcome to next.js!</div>
复制代码

而后,在控制台输入 npm run dev命令,打开 http://localhost:3000便可看到程序已经运行,你固然也可使用其余的端口号,可使用这条命令:npm run dev -- -p <your port here>

目前为止,咱们已经介绍了:

  • 自动编译和打包 (使用 webpackbabel)
  • 代码热更新
  • ./pages目录做为页面渲染目录的的服务器端渲染
  • 静态文件服务(./static/ 被自动定位到 /static/)

想要亲自试试这些到底有多简单, check out sample app - nextgram

代码自动分割

你所声明的每一个 import命令所导入的文件会只会与相关页面进行绑定并提供服务,也就是说,页面不会加载不须要的代码。

import cowsay from 'cowsay-browser'

export default () =>
  <pre> {cowsay.say({ text: 'hi there!' })} </pre>
复制代码

CSS

嵌入式样式 Built-in-CSS

Examples

咱们提供 style-jsx来支持局部独立做用域的 CSS(scope CSS),目的是提供一种相似于 Web组件的 shadow CSS,不过,后者(即shadow CSS)并不支持服务器端渲染(scope CSS是支持的)。

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

译者注:

  1. scope CSS的做用范围,若是添加了 jsx属性,则是不包括子组件的当前组件;若是添加了 globaljsx属性,则是包括了子组件在内的当前组件;若是没添加任何属性,则做用与 添加了 globaljsx的做用相似,只不过 next不会对其进行额外的提取与优化打包
  2. scope CSS的实现原理,其实就是在编译好的代码的对应元素上,添加一个以 jsx开头的类名(class),而后将对应的样式代码提取到此类名下

内联式样式 CSS-in-JS

Examples

几乎可使用全部的内联样式解决方案,最简单一种以下:

export default () => <p style={{ color: 'red' }}>hi there</p>
复制代码

为了使用更多复杂的 CSS-in-JS 内联样式方案,你可能不得不在服务器端渲染的时候强制样式刷新。咱们经过容许自定义包裹着每一个页面的 <Document> 组件的方式来解决此问题。

静态文件服务

在你的项目的根目录新建 static 文件夹,而后你就能够在你的代码经过 /static/ 开头的路径来引用此文件夹下的文件:

export default () => <img src="/static/my-image.png" /> 复制代码

自定义 <head> 头部元素

Examples

咱们暴露了一个用于将元素追加到 <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> 复制代码

注意:当组件卸载的时候,组件内定义的 <Head>将会被清空,因此请确保每一个页面都在其各自的 <Head>内声明了其全部须要的内容,而不是假定这些东西已经在其余页面中添加过了。

译者注:

  1. next 框架自带 <head>标签,做为当前页面的 <head>,若是在组件内自定义了 <Head>,则自定义 <Head>内的元素(例如 <title><meta>等)将会被追加到框架自带的 <head>标签中
  2. 每一个组件自定义的 <Head>内容只会应用在各自的页面上,子组件内定义的 <Head>也会追加到当前页面的 <head>内,若是有重复定义的标签或属性,则子组件覆盖父组件,位于文档更后面的组件覆盖更前面的组件。

数据获取及组件生命周期

Examples

你能够经过导出一个基于 React.Component的组件来获取状态(state)、生命周期或者初始数据(而不是一个无状态函数(stateless),就像上面的一段代码)

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>
    )
  }
}
复制代码

你可能已经注意到了,当加载页面获取数据的时候,咱们使用了一个异步(async)的静态方法 getInitialProps。此静态方法可以获取全部的数据,并将其解析成一个 JavaScript对象,而后将其做为属性附加到 props对象上。

当初始化页面的时候,getInitialProps只会在服务器端执行,而当经过 Link组件或者使用命令路由 API来将页面导航到另一个路由的时候,此方法就只会在客户端执行。

注意:getInitialProps 不能 在子组件上使用,只能应用于当前页面的顶层组件。


若是你在 getInitialProps 中引入了一些只能在服务器端使用的模块(例如一些 node.js的核心模块),请确保经过正确的方式来导入它们 import them properly,不然的话,那极可能会拖慢应用的速度。


你也能够为无状态(stateless)组件自定义 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 - URLpath部分
  • query - URLquery string部分,而且其已经被解析成了一个对象
  • asPath - 在浏览器上展现的实际路径(包括 query字符串)
  • req - HTTP request 对象 (只存在于服务器端)
  • res - HTTP response 对象 (只存在于服务器端)
  • jsonPageRes - 获取的响应数据对象 Fetch Response (只存在于客户端)
  • err - 渲染时发生错误抛出的错误对象

译者注: 基于 getInitialProps在服务器端和客户端的不一样表现,例如 req的存在与否,能够经过此来区分服务器端和客户端。

路由

<Link>

Examples

能够经过 <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> 来让页面在后台同时获取和预加载,以得到最佳的页面加载性能

客户端路由行为与浏览器彻底相同:

  1. 获取组件
  2. 若是组件定义了 getInitialProps,那么进行数据的获取,若是抛出异常,则将渲染_error.js
  3. 在步骤1和步骤2完成后,pushState开始执行,接着新组件将会被渲染

每个顶层组件都会接收到一个 url属性,其包括了如下 API:

  • pathname - 不包括 query字符串在内的当前连接地址的 path字符串(即pathname)
  • query - 当前连接地址的 query字符串,已经被解析为对象,默认为 {}
  • asPath - 在浏览器地址栏显示的当前页面的实际地址(包括 query字符串)
  • push(url, as=url) - 经过 pushState来跳转路由到给定的 url
  • replace(url, as=url) - 经过 replaceState来将当前路由替换到给定的路由地址 url

push 以及 replace的第二个参数 as提供了额外的配置项,当你在服务器上配置了自定义路由的话,那么此参数就会发挥做用。

译者注1: 上面那句话的意思是,as能够根据服务器端路由的配置做出相应的 路由改变,例如,在服务器端,你自定义规定当获取 /apath请求的时候,返回一个位于 /b目录下的页面,则为了配合服务器端的这种指定,你能够这么定义 <Link/>组件: <Link href='/a' as='/b'><a>a</a></Link> 这种作法有一个好处,那就是尽管你将 /a请求指定到了 /b页面,可是由于as的值为 /a,因此编译后的 DOM元素显示的连接的 href值为 /a,可是当真正点击连接时,响应的真正页面仍是 /b


译者注2: <Link>组件主要用于路由跳转功能,其能够接收一个必须的子元素(DOM标签或者纯文字等)

  1. 若是添加的子元素是 DOM元素,则 Link会为此子元素赋予路由跳转功能;
  2. 若是添加的元素是纯文字,则 <Link>默认转化为 a标签,包裹在此文字外部(即做为文字的父元素),若是当前组件有 jsx属性的 scope CSS,这个 a标签是不会受此 scope CSS影响的,也就是说,不会加上以 jsx开头的类名。
    须要注意的是,直接添加纯文字做为子元素的作法现在已经不被同意了(deprecated)。
URL 对象

Examples

<Link>组件能够接收一个 URL对象,此 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>
复制代码

上述代码中 <Link>组件的将会根据 href属性的对象值生成一个 /about?name=ZeitURL字符串,你也能够在此 URL对象中使用任何已经在 Node.js URL module documentation 中定义好了的属性来配置路由。

替换 (replace)而非追加(push)路由 url

<Link>组件默认将新的 URL追加 (push)到路由栈中,但你可使用 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> supports any component that supports the onClick event. In case you don't provide an <a> tag, it will only add the onClick event handler and won't pass the href property. <Link>标签支持全部支持 onClick事件的组件(即只要某组件或者元素标签支持 onClick事件,则 <Link>就可以为其提供跳转路由的功能)。若是你没有给 <Link>标签添加一个 <a>标签的子元素的话,那么它只会执行给定的 onClick事件,而不是执行跳转路由的动做。

// pages/index.js
import Link from 'next/link'

export default () =>
  <div> Click{' '} <Link href="/about"> <img src="/static/image.png" /> </Link> </div> 复制代码
<Link>href暴露给其子元素(child)

若是 <Link>的子元素是一个 <a>标签而且没有指定 href属性的话,那么咱们会自动指定此属性(与 <Link>herf相同)以免重复工做,然而有时候,你可能想要经过一个被包裹在某个容器(例如组件)内的 <a>标签来实现跳转功能,可是 Link并不认为那是一个超连接,所以,就不会把它的 href属性传递给子元素,为了不此问题,你应该给 Link附加一个 passHref属性,强制让 Link将其 href属性传递给它的子元素。

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>
复制代码

命令式路由

Examples

你可使用 next/router来实现客户端侧的页面切换

import Router from 'next/router'

export default () =>
  <div> Click <span onClick={() => Router.push('/about')}>here</span> to read more </div>
复制代码

上述代码中的 Router对象拥有如下 API

  • route - 当前路由字符串
  • pathname - 不包括查询字符串(query string)在内的当前路由的 path(也就是 pathname)
  • query - Object with the parsed query string. Defaults to {}
  • asPath - 在浏览器地址栏显示的当前页面的实际地址(包括 query字符串)
  • push(url, as=url) - 经过 pushState来跳转路由到给定的 url
  • replace(url, as=url) - 经过 replaceState来将当前路由替换到给定的路由地址 url

push 以及 replace的第二个参数 as提供了额外的配置项,当你在服务器上配置了自定义路由的话,那么此参数就会发挥做用。

为了使用编程的方式而不是触发导航和组件获取的方式来切换路由,能够在组件内部使用 props.url.pushprops.url.replace

译者注: 除非特殊须要,不然在组件内部不同意(deprecated)使用 props.url.pushprops.url.replace,而是建议使用 next/router的相关 API

URL 对象

命令式路由 (next/router)所接收的 URL对象与 <Link>URL对象很相似,你可使用相同的方式来pushreplace路由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>
复制代码

命令式路由 (next/router)的 URL对象的属性及其参数的使用方法和 <Link>组件的彻底同样。

路由事件

你还能够监听到与 Router相关的一些事件。

如下是你所可以监听的 Router事件:

  • routeChangeStart(url) - 当路由刚开始切换的时候触发
  • routeChangeComplete(url) - 当路由切换完成时触发
  • routeChangeError(err, url) - 当路由切换发生错误时触发
  • beforeHistoryChange(url) - 在改变浏览器 history以前触发
  • appUpdated(nextRoute) - 当切换页面的时候,应用版本恰好更新的时触发(例如在部署期间切换路由)

Here url is the URL shown in the browser. If you call Router.push(url, as) (or similar), then the value of url will be as. 上面 API中的 url参数指的是浏览器地址栏显示的连接地址,若是你使用 Router.push(url, as)(或者相似的方法)来改变路由,则此值就将是 as的值

下面是一段如何正确地监听路由事件 routeChangeStart的示例代码:

Router.onRouteChangeStart = url => {
  console.log('App is changing to: ', url)
}
复制代码

若是你不想继续监听此事件了,那么你也能够很轻松地卸载掉此监听事件,就像下面这样:

Router.onRouteChangeStart = null
复制代码

若是某个路由加载被取消掉了(例如连续快速地单击两个连接),routeChangeError 将会被执行。此方法的第一个参数 err对象中将包括一个值为 truecancelled属性。

Router.onRouteChangeError = (err, url) => {
  if (err.cancelled) {
    console.log(`Route to ${url} was cancelled!`)
  }
}
复制代码

若是你在一次项目新部署的过程当中改变了路由,那么咱们就没法在客户端对应用进行导航,必需要进行一次完整的导航动做(译者注:意思是没法像正常那样经过 PWA的方式进行导航),咱们已经自动帮你作了这些事。 不过,你也能够经过 Route.onAppUpdated事件对此进行自定义操做,就像下面这样:

Router.onAppUpdated = nextUrl => {
  // persist the local state
  location.href = nextUrl
}
复制代码

译者注:
通常状况下,上述路由事件的发生顺序以下:

  1. routeChangeStart
  2. beforeHistoryChange
  3. routeChangeComplete
浅层路由

Examples

浅层路由(Shallow routing)容许你在不触发 getInitialProps的状况下改变路由(URL),你能够经过要加载页面的 url来获取更新后的 pathnamequery,这样就不会丢失路由状态(state)了。

你能够经过调用 Router.pushRouter.replace,并给它们加上 shallow: true的配置参数来实现此功能,下面是一个使用示例:

// Current URL is "/"
const href = '/?counter=10'
const as = href
Router.push(href, as, { shallow: true })
复制代码

如今,URL已经被更新到了 /?counter=10,你能够在组件内部经过 this.props.url来获取此 URL

你能够在 componentWillReceiveProps钩子函数中获取到 URL的变化,就像下面这样:

componentWillReceiveProps(nextProps) {
  const { pathname, query } = nextProps.url
  // fetch data based on the new query
}
复制代码

注意:

浅层路由只会在某些页面上起做用,例如,咱们能够假定存在另一个名为 about的页面,而后执行下面这行代码:

Router.push('/about?counter=10', '/about?counter=10', { shallow: true })
复制代码

由于这是一个新的页面(/about?counter=10),因此即便咱们已经声明了只执行浅层路由,但当前页面仍然会被卸载掉(unload),而后加载这个新的页面并调用 getInitialProps方法

使用高阶函数 HOC

Examples

若是你想在应用的任何组件都能获取到 router对象,那么你可使用 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)
复制代码

上述代码中的 router对象拥有和 next/router 相同的 API

预获取页面 Prefetching Pages

(下面就是一个小例子)

Examples

Next.js自带容许你预获取(prefetch)页面的 API

由于 Next.js在服务器端渲染页面,因此应用的全部未来可能发生交互的相关连接路径能够在瞬间完成交互,事实上 Next.js能够经过预下载功能来达到一个绝佳的加载性能。[更多详细可见](Read more.)

因为 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 Router from 'next/router'

export default ({ url }) =>
  <div> <a onClick={() => setTimeout(() => url.pushTo('/dynamic'), 100)}> A route transition will happen after 100ms </a> {// but we can prefetch it! Router.prefetch('/dynamic')} </div>
复制代码

自定义服务器和路由

Examples

通常来讲,你可使用 next start命令启动 next服务,可是,你也彻底可使用编程(programmatically)的方式,例如路由匹配等,来定制化路由。

下面就是一个将 /a匹配到 ./page/b,以及将 /b匹配到 ./page/a的例子:

// This file doesn't not 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(path: string, opts: object) - pathNext应用当前的路由位置
  • next(opts: object)

上述 API中的 opt对象存在以下属性:

  • dev (bool) 是否使用开发模式(dev)来启动 Next.js - 默认为 false
  • dir (string) 当前 Next应用的路由位置 - 默认为 '.'
  • quiet (bool) 隐藏包括服务器端消息在内的错误消息 - 默认为 false
  • conf (object) 和next.config.js 中的对象是同一个 - 默认为 {}

而后,将你(在 package.json中配置)的 start命令(script)改写成 NODE_ENV=production node server.js

异步导入 Dynamic Import

Examples

Next.js支持 JavaScript TC39dynamic import proposal规范,因此你能够动态导入(import) JavaScript 模块(例如 React Component)。

你能够将动态导入理解为一种将代码分割为更易管理和理解的方式。 因为 Next.js支持服务器端渲染侧(SSR)的动态导入,因此你能够用它来作一些炫酷的东西。

1. 基本用法(一样支持 SSR

import dynamic from 'next/dynamic'

const DynamicComponent = dynamic(import('../components/hello'))

export default () =>
  <div> <Header /> <DynamicComponent /> <p>HOME PAGE is here!</p> </div>
复制代码

2. 自定义 加载组件

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>
复制代码

3. 禁止 SSR

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>
复制代码

4. 一次性加载多个模块

import dynamic from 'next/dynamic'

const HelloBundle = dynamic({
  modules: props => {
    const components = {
      Hello1: import('../components/hello1'),
      Hello2: import('../components/hello2')
    }

    // Add remove components based on props

    return components
  },
  render: (props, { Hello1, Hello2 }) =>
    <div>
      <h1> {props.title} </h1>
      <Hello1 />
      <Hello2 /> </div>
})

export default () => <HelloBundle title="Dynamic Bundle" /> 复制代码

自定义 <Document>

Examples

Next.js帮你自动跳过了在为页面添加文档标记元素的操做,例如, 你历来不须要主动添加 <html><body>这些文档元素。若是你想重定义这些默认操做的话,那么你能够建立(或覆写) ./page/_ducument.js文件,在此文件中,对 Document进行扩展:

// ./pages/_document.js
import Document, { Head, Main, NextScript } from 'next/document'
import flush from 'styled-jsx/server'

export default class MyDocument extends Document {
  static getInitialProps({ renderPage }) {
    const { html, head, errorHtml, chunks } = renderPage()
    const styles = flush()
    return { html, head, errorHtml, chunks, styles }
  }

  render() {
    return (
      <html> <Head> <style>{`body { margin: 0 } /* custom! */`}</style> </Head> <body className="custom_class"> {this.props.customValue} <Main /> <NextScript /> </body> </html>
    )
  }
}
复制代码

在如下前提下,全部的 getInitialProps 钩子函数接收到的 ctx都指的是同一个对象:

  • 回调函数 renderPage (Function)是真正执行 React渲染逻辑的函数(同步地),这种作法有助于此函数支持一些相似于 Aphrodite'srenderStatic等一些服务器端渲染容器。

注意:<Main/>以外的 React组件都不会被浏览器初始化,若是你想在全部的页面中使用某些组件(例如菜单栏或者工具栏),首先保证不要在其中添加有关应用逻辑的内容,而后能够看看这个例子

译者注: 上面那句话的意思是,在 _document.js文件中,你能够额外添加其余的一些组件,可是这全部的组件中,除了 <Main/>之外,其余的组件内的全部逻辑都不会被初始化和执行,这些不会被初始化和执行的逻辑代码包括除了 render 以外的全部生命周期钩子函数,例如componnetDidMountcomponentWillUpdate,以及一些监听函数,例如 onClickonMouseOver等,因此若是你要在_document.js添加额外的组件,请确保这些组件中除了 render以外没有其余的逻辑

自定义错误处理

客户端和服务器端都会捕获并使用默认组件 error.js来处理 404500错误。若是你但愿自定义错误处理,能够对其进行覆写:

import React from 'react'

export default class Error extends React.Component {
  static getInitialProps({ res, jsonPageRes }) {
    const statusCode = res
      ? res.statusCode
      : jsonPageRes ? jsonPageRes.status : 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-fetch'

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> ) } } 复制代码

若是你想使用自定义的错误页面,那么你能够导入你本身的错误(_error)页面组件而非内置的 next/error

译者注: 若是你只是想覆写默认的错误页面,那么能够在 /pages下新建一个名为 _error.js的文件,Next将使用此文件来覆盖默认的错误页面

自定义配置

为了对 Next.js进行更复杂的自定义操做,你能够在项目的根目录下(和 pages/以及 package.json属于同一层级)新建一个 next.config.js文件

注意:next.confgi.js是一个标准的 Node.js模块,而不是一个 JSON文件,此文件在 Next项目的服务端以及 build阶段会被调用,可是在浏览器端构建时是不会起做用的。

// next.config.js
module.exports = {
  /* config options here */
}
复制代码

设置一个自定义的构建(build)目录

你能够自行指定构建打包的输出目录,例如,下面的配置将会建立一个 build目录而不是 .next做为构建打包的输出目录,若是没有特别指定的话,那么默认就是 .next

// next.config.js
module.exports = {
  distDir: 'build'
}
复制代码

Configuring the onDemandEntries

Next 暴露了一些可以让你本身控制如何部署服务或者缓存页面的配置:

module.exports = {
  onDemandEntries: {
    // 控制页面在内存`buffer`中缓存的时间,单位是 ms
    maxInactiveAge: 25 * 1000,
    // number of pages that should be kept simultaneously without being disposed
    pagesBufferLength: 2,
  }
}
复制代码

自定义 webpack 配置

Examples

你能够经过 next.config.js中的函数来扩展 webpack的配置

// This file is not going through babel transformation.
// So, we write it in vanilla JS
// (But you could use ES2015 features supported by your Node.js version)

module.exports = {
  webpack: (config, { buildId, dev }) => {
    // 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的配置中添加一个支持新文件类型(css less svg等)的 loader,由于 webpack只会打包客户端代码,因此(loader)不会在服务器端的初始化渲染中起做用。Babel是一个很好的替代品,由于其给服务器端和客户端提供一致的功能效果(例如,babel-plugin-inline-react-svg)。

自定义 Babel 配置

Examples

为了扩展对 Babel的使用,你能够在应用的根目录下新建 .babelrc文件,此文件是非必须的。 若是此文件存在,那么咱们就认为这个才是真正的Babel配置文件,所以也就须要为其定义一些 next项目须要的东西, 并将之当作是next/babel的预设配置(preset) 这种设计是为了不你有可能对咱们可以定制 babel配置而感到诧异。

下面是一个 .babelrc文件的示例:

{
  "presets": ["next/babel", "stage-0"]
}
复制代码

CDN 支持

你能够设定 assetPrefix项来配置 CDN源,以便可以与 Next.js项目的 host保持对应。

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将会自动使用所加载脚本的 CDN域(做为项目的 CDN域),可是对 /static目录下的静态文件就无能为力了。若是你想让那些静态文件也能用上CDN,那你就不得不要本身指定 CDN域,有种方法也可让你的项目自动根据运行环境来肯定 CDN域,能够看看这个例子

项目部署

构建打包和启动项目被分红了如下两条命令:

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也可使用其余的托管方案,更多详细能够看一下这部份内容 'Deployment' 注意:咱们推荐你推送 .next,或者你自定义的打包输出目录(到托管方案上)(Please have a look at 'Custom Config',你还能够自定义一个专门用于放置配置文件(例如 .npmignore.gitignore)的文件夹。不然的话,使用 files或者 now.files来选择要部署的白名单(很明显要排除掉 .next或你自定义的打包输出目录)

导出静态 HTML 页面

Examples

你能够将你的 Next.js应用当成一个不依赖于 Node.js服务的静态应用。此静态应用支持几乎全部的 Next.js特性,包括 异步导航、预获取、预加载和异步导入等。

使用

首先,Next.js的开发工做没什么变化,而后建立一个 Next.js的配置文件 config,就像下面这样:

// next.config.js
module.exports = {
  exportPathMap: function() {
    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' } }
    }
  }
}
复制代码

须要注意的是,若是声明的路径表示的是一个文件夹的话,那么最终将会导出一份相似于 /dir-name/index.html的文件,若是声明的路径是一个文件的话,那么最终将会以指定的文件名导出,例如上述代码中,就会导出一个 readme.md的文件。若是你使用了一个不是以 .html结尾的文件,那么在解析此文件的时候,你须要给 text/html设置一个 Content-Type头(header)

经过上述的相似代码,你能够指定你想要导出的静态页面。

接着,输入如下命令:

next build
next export
复制代码

或许,你还能够在 package.json文件中多添加一条命令:

{
  "scripts": {
    "build": "next build && next export"
  }
}
复制代码

如今就只须要输入这一条命令就好了:

npm run build
复制代码

这样,你在 out目录下就有了一个当前应用的静态网站了。

你也能够自定义输出目录,更多帮助能够在命令行中输入 next export -h 获取

如今,你就能够把输出目录(例如/out)部署到静态文件服务器了,须要注意的是,若是你想要部署到 Github上的话,那么须要须要增长一个步骤

例如,只须要进入 out目录,而后输入如下命令,就能够把你的应用部署到 ZEIT now

now
复制代码

局限性

当你输入 next export命令时,咱们帮你构建了应用的 HTML静态版本,在此阶段,咱们将会执行页面中的 getInitialProps函数。

因此,你只能使用 context对象传递给 getInitialPropspathnamequeryasPath 字段,而 reqres则是不可用的(resres只在服务器端可用)。

基于此,你也没法在咱们预先构建 HTML文件的时候,动态的呈现 HTML页面,若是你真的想要这么作(指动态构建页面)的话,请使用 next start

相关技巧

FAQ

可用在生产环境使用吗? https://zeit.co 就是使用 `Next.js`构建的。

不管是开发者体验仍是终端表现,它都超出预期,因此咱们决定将它共享到社区中。

体积有多大?

客户端包的大小根据每一个应用程序的功能等不一样而不尽相同。 一个最简单的 Next程序包在 gzip压缩后可能只有 65kb 大小。

它(指Next.js) 和 `create-react-app`是差很少的吗?

是也不是。 说是,是由于两者都让你的开发变得更轻松。 说不是,则是由于 Next.js强制规定了一些目录结构,以便咱们能实现更多高级的操做,例如:

  • 服务器端渲染(SSR)
  • 代码自动分割

此外,Next.js还内置了两个对于单页应用来讲比较重要的特性:

  • Routing with lazy component loading: <Link> (by importing next/link)
  • 修改 <head>元素的方法(经过导入 next/head)

若是你想在 Next.js或其余 React应用中复用组件,则使用 create-react-app是一个很好的选择,你能够稍后将其导入以保证代码库的纯净。

如何使用嵌入式CSS(`CSS-in-JS`)方案?

Next.js自带的库 styled-jsx支持 局部(scoped)css,固然,你也能够在 Next应用中添加上面所提到的任何你喜欢的代码库来使用你想要的 CSS-in-JS解决方案。

如何使用相似于 SASS / SCSS / LESS 之类的 CSS 预处理器?

Next.js自带的库 styled-jsx支持 局部(scoped)css,固然,你也能够在 Next应用中使用如下示例中的任何一种 CSS预处理器方案:

What syntactic features are transpiled? How do I change them?

(语法特性)咱们参照 V8引擎,由于 V8普遍支持 ES6async 以及 await,因此咱们也就支持这些,由于 V8还不支持类装饰器(class decorator),因此咱们也就不支持它(类装饰器)

能够看看 这些 以及 这些

Why a new Router?

Next.js is special in that:

  • Routes don’t need to be known ahead of time
  • Routes are always lazy-loadable
  • Top-level components can define getInitialProps that should block the loading of the route (either when server-rendering or lazy-loading)

基于上述几个特色,咱们可以构造出一个具备如下两个功能的简单路由:

  • 每一个顶级组件都会接收到一个 url对象来检查 url 或者 修改历史记录
  • <Link />组件做为相似于 <a/>等标签元素的容器以便进行客户端的页面切换。

咱们已经在一些颇有意思的场景下测试了路由的灵活性,更多相信能够看这里 nextgram

如何自定义路由?

Next.js提供了一个 request handler,利用其咱们可以让任意 URL与 任何组件之间产生映射关系。 在客户端,<Link />组件有个 as属性,它可以改变获取到的 URL

如何获取数据?

这由你决定, getInitialProps 是一个 异步(async)函数(或者也能够说是一个返回 Promise的标准函数),它可以从任意位置获取数据。

可以配合使用 `GraphQL`吗

固然,这还有个用 Apollo 的例子呢。

可以配合使用 `Redux`吗?

固然,这也有个例子

为何我不能在开发服务器中访问个人静态导出路由呢?

这是一个已知的 Next.js架构问题,在解决方案还没内置到框架中以前,你能够先看看这一个例子中的解决方法来集中管理你的路由。

我能够在 Next应用中使用我喜欢的 JavaScript库或工具包吗?

咱们在发布初版的时候就已经提供了不少例子,你能够看看这个目录

大家是怎么作出这个框架的?

咱们力求达到的目标大部分都是从 由 Guillermo Rauch给出的[设计富Web应用的 7个原则]中受到启发,PHP的易用性也是一个很棒的灵感来源,咱们以为在不少你想使用 PHP来输出 HTML页面的状况下,Next.js都是一个很好的替代品,不过不像 PHP,咱们从 ES6的模块化系统中得到好处,每一个文件都能很轻松地导入一个可以用于延迟求值或测试的组件或函数。

当咱们研究 React的服务器端渲染时,咱们并无作出太大的改变,由于咱们偶然发现了 React做者 Jordan Walke写的 react-page (now deprecated)。

Contributing

Please see our contributing.md

Authors

相关文章
相关标签/搜索