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