Next.js 是一個輕量級的 React 服務端渲染應用框架。javascript
在項目文件夾中運行:css
npm install --save next react react-dom
將下面腳本添加到 package.json 中:html
{ "scripts": { "dev": "next", "build": "next build", "start": "next start" } }
下面, 文件系統是主要的 API. 每一個.js
文件將變成一個路由,自動處理和渲染。前端
新建 ./pages/index.js
到你的項目中:java
export default () => <div>Welcome to next.js!</div>
運行 npm run dev
命令並打開 http://localhost:3000
。 要使用其餘端口,你能夠運行 npm run dev -- -p <your port here>
.node
到目前爲止,咱們作到:react
./pages
做爲服務的渲染和索引./public/
映射到 /
(能夠 建立一個靜態目錄 在你的項目中)這裏有個簡單的案例,能夠下載看看 sample app - nextgramwebpack
每一個頁面只會導入import
中綁定以及被用到的代碼. 這意味着頁面不會加載沒必要要的代碼git
import cowsay from 'cowsay-browser' export default () => <pre>{cowsay.say({ text: 'hi there!' })}</pre>
咱們綁定 styled-jsx 來生成獨立做用域的 CSS. 目標是支持 "shadow CSS",可是 不支持獨立模塊做用域的 JS.github
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.
有些狀況可使用 CSS 內嵌 JS 寫法。以下所示:
export default () => <p style={{ color: 'red' }}>hi there</p>
更復雜的內嵌樣式解決方案,特別是服務端渲染時的樣式更改。咱們能夠經過包裹自定義 Document,來添加樣式,案例以下:custom <Document>
支持用.css
, .scss
, .less
or .styl
,須要配置默認文件 next.config.js,具體可查看下面連接
在根目錄下新建文件夾叫public
。代碼能夠經過/
來引入相關的靜態資源。
export default () => <img src="/my-image.png" alt="my image" />
注意:不要自定義靜態文件夾的名字,只能叫public
,由於只有這個名字 Next.js 纔會把它看成靜態資源。
<head>
<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" /> </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>
定義了所須要的內容,而不是假設其餘頁面已經加過了
當你須要狀態,生命週期鉤子或初始數據填充時,你能夠導出React.Component
(而不是上面的無狀態函數),以下所示:
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
頁面中使用。
只有服務端用到的模塊放在
getInitialProps
裏,請確保正確的導入了它們,可參考import them properly。
不然會拖慢你的應用速度。
你也能夠給無狀態組件定義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
- 渲染過程當中的任何錯誤
Next.js 不會隨應用程序中每一個可能的路由一塊兒發佈路由清單,所以當前頁面不知道客戶端上的任何其餘頁面。出於可擴展性考慮,全部後續路由都會惰性加載。
<Link>
用法能夠用 <Link>
組件實現客戶端的路由切換。
基本例子
參考下面的兩個頁面:
// pages/index.js import Link from 'next/link' function Home() { return ( <div> Click{' '} <Link href="/about"> <a>here</a> </Link>{' '} to read more </div> ) } export default Home
// pages/about.js function About() { return <p>Welcome to About!</p> } export default About
自定義路由 (使用 URL 中的 props)
<Link>
組件有兩個主要屬性:
href
: pages
目錄內的路徑+查詢字符串.as
: 將在瀏覽器 URL 欄中呈現的路徑.例子:
假設你有個這樣的路由 /post/:slug
.
你能夠建立文件 pages/post.js
class Post extends React.Component { static async getInitialProps({ query }) { console.log('SLUG', query.slug) return {} } render() { return <h1>My blog post</h1> } } export default Post
express
(或者其餘服務端) 的 server.js
文件 (這僅適用於 SSR). 這將解析/post/:slug
到pages/post.js
並在 getInitialProps 中提供slug
做爲查詢的一部分。server.get('/post/:slug', (req, res) => { return app.render(req, res, '/post', { slug: req.params.slug }) })
next/link
:<Link href="/post?slug=something" as="/post/something">
注意:可使用<Link prefetch>
使連接和預加載在後臺同時進行,來達到頁面的最佳性能。
客戶端路由行爲與瀏覽器很類似:
getInitialProps
,則獲取數據。若是有錯誤狀況將會渲染 _error.js
。pushState
執行,新組件被渲染。若是須要注入pathname
, query
或 asPath
到你組件中,你可使用withRouter。
組件<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>
你也能夠用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 調用pushState
replace(url, as=url)
- 用給定的 url 調用replaceState
beforePopState(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)
若是路由加載被取消(好比快速連續雙擊連接),routeChangeError
將觸發。傳遞 err,而且屬性 cancelled 的值爲 true。
Router.events.on('routeChangeError', (err, url) => { if (err.cancelled) { console.log(`Route to ${url} was cancelled!`) } })
淺層路由容許你改變 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
。
若是你想應用裏每一個組件都處理路由對象,你可使用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
.
⚠️ 只有生產環境纔有此功能 ⚠️
Next.js 有容許你預加載頁面的 API。
用 Next.js 服務端渲染你的頁面,能夠達到全部你應用裏全部將來會跳轉的路徑即時響應,有效的應用 Next.js,能夠經過預加載應用程序的功能,最大程度的初始化網站性能。查看更多.
Next.js 的預加載功能只預加載 JS 代碼。當頁面渲染時,你可能須要等待數據請求。
<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> )
大多數預加載是經過 處理的,可是咱們還提供了命令式 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)
通常你使用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}`) }) })
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>
組件來初始化頁面。你能夠重寫它來控制頁面初始化,以下面的事:
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>
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
組件代替。
renderPage
🚧 應該注意的是,您應該定製「renderPage」的惟一緣由是使用 css-in-js 庫,須要將應用程序包裝起來以正確使用服務端渲染。 🚧
import Document from 'next/document' class MyDocument extends Document { static async getInitialProps(ctx) { const originalRenderPage = ctx.renderPage ctx.renderPage = () => originalRenderPage({ // useful for wrapping the whole react tree enhanceApp: App => App, // useful for wrapping in a per-page basis enhanceComponent: Component => Component, }) // Run the parent `getInitialProps` using `ctx` that now includes our custom `renderPage` const initialProps = await Document.getInitialProps(ctx) return initialProps } } export default MyDocument
404 和 500 錯誤客戶端和服務端都會經過error.js
組件處理。若是你想改寫它,則新建_error.js
在文件夾中:
⚠️ 該pages/_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' }, }
可使用些一些常見的模塊
注意: 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
.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 }, }
爲了擴展方便咱們使用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.js 支持 2 種提供配置的方式:
構建時配置的工做方式是將提供的值內聯到 Javascript 包中。
你能夠在next.config.js
設置env
:
// next.config.js module.exports = { env: { customKey: 'value', }, }
這將容許你在代碼中使用process.env.customKey
,例如:
// pages/index.js function Index() { return <h1>The value of customKey is: {process.env.customKey}</h1> } export default Index
⚠️ 請注意,使用此選項時不可用
target: 'serverless'
⚠️ 一般,您但願使用構建時配置來提供配置。緣由是運行時配置增長了一個小的 rendering/initialization 開銷。
next/config
模塊使你應用運行時能夠讀取些存儲在next.config.js
的配置項。serverRuntimeConfig
屬性只在服務器端可用,publicRuntimeConfig
屬性在服務端和客戶端可用。
// next.config.js module.exports = { serverRuntimeConfig: { // Will only be available on the server side mySecret: 'secret', secondSecret: process.env.SECOND_SECRET, // Pass through env variables }, publicRuntimeConfig: { // Will be available on both server and client staticFolder: '/static', }, }
// 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 function MyImage() { return ( <div> <img src={`${publicRuntimeConfig.staticFolder}/logo.png`} alt="logo" /> </div> ) } export default MyImage
啓動開發環境服務能夠設置不一樣的 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
或自定義打包文件夾。
無服務器部署經過將應用程序拆分爲更小的部分(也稱爲lambdas)來顯着提升可靠性和可伸縮性。在 Next.js 中,pages
目錄中的每一個頁面都變成了無服務器的 lambda。
對於無服務器的人來講,有許多好處。引用的連接在 Express 的上下文中討論了其中的一些,但這些原則廣泛適用:無服務器容許分佈式故障點,無限的可擴展性,而且經過「爲您使用的內容付費」的模式來提供難以置信的價格。
要在 Next.js 中啓用無服務器模式,可在Next.config.js
中配置target
值爲serverless
:
// next.config.js module.exports = { target: 'serverless', }
serverless
將每頁輸出一個 lambda。此文件是徹底獨立的,不須要運行任何依賴項:
pages/index.js
=> .next/serverless/pages/index.js
pages/about.js
=> .next/serverless/pages/about.js
Next.js 無服務器功能的簽名相似於 Node.js HTTP 服務器回調:
export function render(req: http.IncomingMessage, res: http.ServerResponse) => void
void
指的是沒有返回值的函數,它等同於 JavaScriptundefined
。調用該函數將完成請求。使用無服務配置, 你能夠講 Next.js 部署到ZEIT Now 並提供全部的好處和易於控制; custom routes 緩存頭. 要了解更多信息,請參閱 ZEIT Guide for Deploying Next.js with Now
Next.js 爲無服務器部署提供低級 API,由於託管平臺具備不一樣的功能簽名。一般,您須要使用兼容性層包裝 Next.js 無服務器構建的輸出。
例如,若是平臺支持 Node.jshttp.Server
類:
const http = require('http') const page = require('./.next/serverless/pages/about.js') const server = new http.Server((req, res) => page.render(req, res)) server.listen(3000, () => console.log('Listening on http://localhost:3000'))
有關特定平臺示例,請參閱the examples section above.
pages
目錄中的每一個頁面都成爲無服務器功能(lambda)target: 'serverless'
in next.config.js
next.config.js
,請注意這意味着publicRuntimeConfig
/ serverRuntimeConfig
不支持。Next.js 支持 IE11 和全部的現代瀏覽器使用了@babel/preset-env
。爲了支持 IE11,Next.js 須要全局添加Promise
的 polyfill。有時你的代碼或引入的其餘 NPM 包的部分功能現代瀏覽器不支持,則須要用 polyfills 去實現。
ployflls 實現案例爲polyfills。
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
若是您必須複製 robots.txt 等自定義文件或生成 sitemap.xml,您能夠在其中執行此操做exportPathMap
。 exportPathMap
獲取一些上下文參數來幫助您建立/複製文件:
dev
- true
表示在開發環境下使用exportPathMap
. false
表示運行於next export
. 在開發中,「exportpathmap」用於定義路由,不須要複製文件等行爲。dir
- 項目目錄的絕對路徑outDir
- 指向out
目錄的絕對路徑(可配置爲-o
或--outdir
)。當dev
爲true
時,outdir
的值將爲null
。distDir
- .next
目錄的絕對路徑(可以使用distDir
配置鍵配置)buildId
- 導出正在運行的 buildId// next.config.js const fs = require('fs') const { join } = require('path') const { promisify } = require('util') const copyFile = promisify(fs.copyFile) module.exports = { exportPathMap: async function( defaultPathMap, { dev, dir, outDir, distDir, buildId } ) { if (dev) { return defaultPathMap } // This will copy robots.txt from your project root into the out directory await copyFile(join(dir, 'robots.txt'), join(outDir, 'robots.txt')) return defaultPathMap }, }
使用next export
,咱們建立了個靜態 HTML 應用。構建時將會運行頁面裏生命週期getInitialProps
函數。
req
和res
只在服務端可用,不能經過getInitialProps
。
因此你不能預構建 HTML 文件時動態渲染 HTML 頁面。若是你想動態渲染能夠運行
next start
或其餘自定義服務端 API。
一個 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 頁面
咱們實現的大部分目標都是經過 Guillermo Rauch 的Web 應用的 7 原則來啓發出的。
PHP 的易用性也是個很好的靈感來源,咱們以爲 Next.js 能夠替代不少須要用 PHP 輸出 HTML 的場景。
與 PHP 不一樣的是,咱們得利於 ES6 模塊系統,每一個文件會輸出一個組件或方法,以即可以輕鬆的導入用於懶加載和測試
咱們研究 React 的服務器渲染時並無花費很大的步驟,由於咱們發現一個相似於 Next.js 的產品,React 做者 Jordan Walke 寫的react-page (如今已經廢棄)