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

相關文章
相關標籤/搜索