资讯专栏INFORMATION COLUMN

Nextjs中文文档

luckyw / 1853人阅读

摘要:中文站点中文站当前翻译版本为。注意将不能使用在子组件中。只能使用在页面中。替换路由组件默认将新推入路由栈中。以防服务端渲染发生错误,建议事件写在生命周期里。禁止文件路由默认情况,将会把下的所有文件匹配路由如渲染为如果你的项目使用

Next.js 是一个轻量级的 React 服务端渲染应用框架。

Next.js中文站点 http://nextjs.frontendx.cn

Next.js中文站Github https://github.com/raoenhui/next-site-cn

当前翻译版本为 7.0.0-canary.8。

怎么使用

安装

代码自动分割

CSS

支持嵌入样式

内嵌样式

使用 CSS / Sass / Less / Stylus files

静态文件服务(如图像)

获取数据以及组件生命周期

路由

用法

URL 对象

替换路由

组件支持点击事件onClick

暴露href给子元素

禁止滚动到页面顶部

命令式

拦截器 popstate

URL 对象用法

路由事件

浅层路由

高阶组件

预加载页面

用法

命令式 prefetch 写法

自定义服务端路由

禁止文件路由

动态前缀

动态导入

1. 基础支持 (同样支持 SSR)

2. 自定义加载组件

3. 禁止使用 SSR

4. 同时加载多个模块

自定义

自定义

自定义错误处理

渲染内置错误页面

自定义配置

设置自定义构建目录

禁止 etag 生成

配置 onDemandEntries

配置页面后缀名解析扩展

配置构建 ID

自定义 webpack 配置

自定义 babel 配置

暴露配置到服务端和客户端

启动服务选择 hostname

CDN 支持前缀

项目部署

浏览器支持

导出静态页面

使用

限制

多 zone

怎么定义一个 zone

怎么合并他们

技巧

FAQ

贡献

作者

怎么使用

安装

安装它:

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 () => 
Welcome to next.js!

运行 npm run dev 命令并打开 http://localhost:3000。 如果你想使用其他端口,可运行 npm run dev -- -p <设置端口号>.

目前为止我们可以了解到:

自动打包编译 (使用 webpack 和 babel)

热加载

./pages作为服务端的渲染和索引

Static file serving. ./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 () =>
  
    {cowsay.say({ text: "hi there!" })}
  

CSS

支持嵌入样式


Examples
  • Basic css

我们绑定 styled-jsx 来生成独立作用域的 CSS. 目标是支持 "shadow CSS",但是 不支持独立模块作用域的 JS.

export default () =>
  
Hello world

scoped!

想查看更多案例可以点击 styled-jsx documentation查看.

内嵌样式


Examples

  • Styled components
  • Styletron
  • Glamor
  • Glamorous
  • Cxs
  • Aphrodite
  • Fela

有些情况可以使用 CSS 内嵌 JS 写法。如下所示:

export default () => 

hi there

更复杂的内嵌样式解决方案,特别是服务端渲染的时样式更改。我们可以通过包裹自定义 Document,来添加样式,案例如下:custom

使用 CSS / Sass / Less / Stylus files

支持用.css, .scss, .less or .styl,需要配置默认文件 next.config.js,具体可查看下面链接

@zeit/next-css

@zeit/next-sass

@zeit/next-less

@zeit/next-stylus

静态文件服务(如图像)

在根目录下新建文件夹叫static。代码可以通过/static/来引入相关的静态资源。

export default () => 

_注意:不要自定义静态文件夹的名字,只能叫static ,因为只有这个名字 Next.js 才会把它当作静态资源。

生成


Examples

我们设置一个内置组件来装载到页面中。

import Head from "next/head"

export default () =>
  
My page title

Hello world!

我们定义key属性来避免重复的标签,保证只渲染一次,如下所示:

import Head from "next/head"
export default () => (
  
My page title

Hello world!

)

只有第二个才被渲染。

注意:在卸载组件时,的内容将被清除。请确保每个页面都在其定义了所需要的内容,而不是假设其他页面已经加过了

获取数据以及组件生命周期


Examples
  • Data fetch

如果你需要一个有状态、生命周期或有初始数据的 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 (
      
Hello World {this.props.userAgent}
) } }

相信你注意到,当页面渲染时加载数据,我们使用了一个异步方法getInitialProps。它能异步获取 JS 普通对象,并绑定在props

当服务渲染时,getInitialProps将会把数据序列化,就像JSON.stringify。所以确保getInitialProps返回的是一个普通 JS 对象,而不是Date, MapSet类型。

当页面初次加载时,getInitialProps只会在服务端执行一次。getInitialProps只有在路由切换的时候(如Link组件跳转或路由自定义跳转)时,客户端的才会被执行。

当页面初始化加载时,getInitialProps只会加载在服务端。只有当路由跳转(Link组件跳转或 API 方法跳转)时,客户端才会执行getInitialProps

注意:getInitialProps将不能使用在子组件中。只能使用在pages页面中。


只有服务端用到的模块放在getInitialProps里,请确保正确的导入了它们,可参考import them properly。
否则会拖慢你的应用速度。


你也可以给无状态组件定义getInitialProps

const Page = ({ stars }) =>
  
Next stars: {stars}
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 - 渲染过程中的任何错误

路由

用法


Examples

可以用 组件实现客户端的路由切换。

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

export default () =>
  
Click{" "} here {" "} to read more
// pages/about.js
export default () => 

Welcome to About!

注意:可以使用使链接和预加载在后台同时进行,来达到页面的最佳性能。

客户端路由行为与浏览器很相似:

组件获取

如果组件定义了getInitialProps,数据获取了。如果有错误情况将会渲染 _error.js

1和2都完成了,pushState执行,新组件被渲染。

如果需要注入pathname, queryasPath到你组件中,你可以使用withRouter。

URL 对象


Examples

组件接收 URL 对象,而且它会自动格式化生成 URL 字符串

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

export default () =>
  
Click{" "} here {" "} to read more

将生成 URL 字符串/about?name=Zeit,你可以使用任何在Node.js URL module documentation定义过的属性。

替换路由

组件默认将新 url 推入路由栈中。你可以使用replace属性来防止添加新输入。

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

export default () =>
  
Click{" "} here {" "} to read more

组件支持点击事件 onClick

支持每个组件所支持的onClick事件。如果你不提供标签,只会处理onClick事件而href将不起作用。

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

export default () =>
  
Click{" "}

暴露 href 给子元素

如子元素是一个没有 href 属性的标签,我们将会指定它以免用户重复操作。然而有些时候,我们需要里面有标签,但是Link组件不会被识别成超链接,结果不能将href传递给子元素。在这种场景下,你可以定义一个Link组件中的布尔属性passHref,强制将href传递给子元素。

注意: 使用a之外的标签而且没有通过passHref的链接可能会使导航看上去正确,但是当搜索引擎爬行检测时,将不会识别成链接(由于缺乏 href 属性),这会对你网站的 SEO 产生负面影响。

import Link from "next/link"
import Unexpected_A from "third-library"

export default ({ href, name }) =>
  
    
      {name}
    
  

禁止滚动到页面顶部

的默认行为就是滚到页面顶部。当有 hash 定义时(#),页面将会滚动到对应的 id 上,就像标签一样。为了预防滚动到顶部,可以给
scroll={false}属性:

Disables scrolling
Changes with scrolling to top

命令式


Examples

你也可以用next/router实现客户端路由切换

import Router from "next/router"

export default () =>
  
Click Router.push("/about")}>here to read more

拦截器 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 的页面,浏览器栏显示的是第二个参数 url

replace(url, as=url) - performs a replaceState call with the given url

beforePopState(cb=function) - 在路由器处理事件之前拦截.

pushreplace 函数的第二个参数as,是为了装饰 URL 作用。如果你在服务器端设置了自定义路由将会起作用。

URL 对象用法

pushreplace可接收的 URL 对象(组件的 URL 对象一样)来生成 URL。

import Router from "next/router"

const handler = () =>
  Router.push({
    pathname: "/about",
    query: { name: "Zeit" }
  })

export default () =>
  
Click here to read more

也可以像组件一样添加额外的参数。

路由事件

你可以监听路由相关事件。
下面是事件支持列表:

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!`)
  }
})

浅层路由


Examples

浅层路由允许你改变 URL 但是不执行getInitialProps生命周期。你可以加载相同页面的 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.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

高阶组件


Examples

如果你想应用里每个组件都处理路由对象,你可以使用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 (
    
      {children}
    
  )
}

export default withRouter(ActiveLink)

上面路由对象的 API 可以参考next/router.

预加载页面

⚠️ 只有生产环境才有此功能 ⚠️


Examples
  • Prefetching

Next.js 有允许你预加载页面的 API。

用 Next.js 服务端渲染你的页面,可以达到所有你应用里所有未来会跳转的路径即时响应,有效的应用 Next.js,可以通过预加载应用程序的功能,最大程度的初始化网站性能。查看更多.

Next.js 的预加载功能只预加载 JS 代码。当页面渲染时,你可能需要等待数据请求。

用法

你可以给添加 prefetch 属性,Next.js 将会在后台预加载这些页面。

import Link from "next/link"

// example header component
export default () =>
  

命令式 prefetch 写法

大多数预加载是通过处理的,但是我们还提供了命令式 API 用于更复杂的场景。

import { withRouter } from "next/router"

export default withRouter(({ router }) =>
  
)

路由实例只允许在应用程序的客户端。以防服务端渲染发生错误,建议 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 (
          
    )
  }
}

export default withRouter(MyLink)

自定义服务端路由


Examples

一般你使用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}`)
  })
})

动态导入


Examples

ext.js 支持 JavaScript 的 TC39 提议dynamic import proposal。你可以动态导入 JavaScript 模块(如 React 组件)。

动态导入相当于把代码分成各个块管理。Next.js 服务端动态导入功能,你可以做很多炫酷事情。

下面介绍一些动态导入方式:

1. 基础支持 (同样支持 SSR)
import dynamic from "next/dynamic"

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

export default () =>
  

HOME PAGE is here!

2. 自定义加载组件
import dynamic from "next/dynamic"

const DynamicComponentWithCustomLoading = dynamic(
  import("../components/hello2"),
  {
    loading: () => 

...

} ) export default () =>

HOME PAGE is here!

3. 禁止使用 SSR
import dynamic from "next/dynamic"

const DynamicComponentWithNoSSR = dynamic(import("../components/hello3"), {
  ssr: false
})

export default () =>
  

HOME PAGE is here!

4. 同时加载多个模块
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 }) =>
    

{props.title}

}) export default () =>

自定义


Examples
  • Using _app.js for layout

  • Using _app.js to override componentDidCatch

组件来初始化页面。你可以重写它来控制页面初始化,如下面的事:

当页面变化时保持页面布局

当路由变化时保持页面状态

使用componentDidCatch自定义处理错误

注入额外数据到页面里 (如 GraphQL 查询)

重写的话,新建./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 
      
    
  }
}

自定义


Examples
  • Styled components custom document

  • Google AMP

在服务端呈现

初始化服务端时添加文档标记元素

通常实现服务端渲染会使用一些 css-in-js 库,如styled-components, glamorous 或 emotion。styled-jsx是 Next.js 自带默认使用的 css-in-js 库

Next.js会自动定义文档标记,比如,你从来不需要添加, 等。如果想自定义文档标记,你可以新建./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 (
      
        
          
        
        
          
) } }

钩子getInitialProps接收到的参数ctx对象都是一样的

回调函数renderPage是会执行 React 渲染逻辑的函数(同步),这种做法有助于此函数支持一些类似于 Aphrodite 的 renderStatic 等一些服务器端渲染容器。

注意:

外的 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 (
      

{this.props.statusCode ? `An error ${this.props.statusCode} occurred on server` : "An error occurred on client"}

) } }

渲染内置错误页面

如果你想渲染内置错误页面,你可以使用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 
    }

    return (
      
Next stars: {this.props.stars}
) } }
如果你自定义了个错误页面,你可以引入自己的错误页面来代替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 生成

你可以禁止 etag 生成根据你的缓存策略。如果没有配置,Next 将会生成 etags 到每个页面中。

// next.config.js
module.exports = {
  generateEtags: false
}

配置 onDemandEntries

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"]
}

配置构建 ID

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 配置


Examples
  • Custom webpack bundle analyzer

可以使用些一些常见的模块

@zeit/next-css

@zeit/next-sass

@zeit/next-less

@zeit/next-preact

@zeit/next-typescript

注意: 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
  }
}

自定义 babel 配置


Examples
  • Custom babel configuration

为了扩展方便我们使用babel,可以在应用根目录新建.babelrc文件,该文件可配置。

如果有该文件,我们将会考虑数据源,因此也需要定义 next 项目需要的东西,也就是 next/babel预设。

这种设计方案将会使你不诧异于我们可以定制 babel 配置。

下面是.babelrc文件案例:

{
  "presets": ["next/babel"],
  "plugins": []
}

next/babel预设可处理各种 React 应用所需要的情况。包括:

preset-env

preset-react

plugin-proposal-class-properties

plugin-proposal-object-rest-spread

plugin-transform-runtime

styled-jsx

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 () => 

启动服务选择 hostname

启动开发环境服务可以设置不同的 hostname,你可以在启动命令后面加上--hostname 主机名-H 主机名。它将会启动一个 TCP 服务器来监听连接所提供的主机。

CDN 支持前缀

建立一个 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中。否则,使用filesnow.files
添加部署白名单,并排除.next或自定义打包文件夹。

浏览器支持

Next.js 支持 IE11 和所有的现代浏览器使用了@babel/preset-env。为了支持 IE11,Next.js 需要全局添加Promise的 polyfill。有时你的代码或引入的其他 NPM 包的部分功能现代浏览器不支持,则需要用 polyfills 去实现。

ployflls 实现案例为polyfills。

导出静态页面


Examples
  • Static export

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 函数。

reqres只在服务端可用,不能通过getInitialProps

所以你不能预构建 HTML 文件时动态渲染 HTML 页面。如果你想动态渲染可以运行next start或其他自定义服务端 API。

多 zone


Examples
  • With Zones

一个 zone 时一个多带带的 Next.js 应用。如果你有很多 zone,你可以合并成一个应用。

例如,你如下有两个 zone:

https://docs.my-app.com 服务于路由 /docs/**

https://ui.my-app.com 服务于所有页面

有多 zone 应用技术支持,你可以将几个应用合并到一个,而且可以自定义 URL 路径,使你能同时多带带开发各个应用。

与 microservices 观念类似, 只是应用于前端应用.

怎么定义一个 zone

zone 没有多带带的 API 文档。你需要做下面事即可:

确保你的应用里只有需要的页面 (例如, https://ui.my-app.com 不包含 /docs/**)

确保你的应用有个前缀assetPrefix。(你也可以定义动态前缀dynamically)

怎么合并他们

你能使用 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 页面

技巧

设置301重定向

只处理服务器端模块

构建项目 React-Material-UI-Next-Express-Mongoose-Mongodb

构建一个 SaaS 产品 React-Material-UI-Next-MobX-Express-Mongoose-MongoDB-TypeScript

问答


这个产品可以用于生产环境吗?

https://zeit.co 都是一直用 Next.js 写的。

它的开发体验和终端用户体验都很好,所以我们决定开源出来给大家共享。



体积多大?

客户端大小根据应用需求不一样大小也不一样。

一个最简单 Next 应该用 gzip 压缩后大约65kb



这个像 create-react-app?

是或不是.

是,因为它让你的 SSR 开发更简单。

不是,因为它规定了一定的目录结构,使我们能做以下更高级的事:

服务端渲染

自动代码分割

此外,Next.js 还提供两个内置特性:

路由与懒加载组件: (通过引入 next/link)

修改的组件: (通过引入 next/head)

如果你想写共用组件,可以嵌入 Next.js 应用和 React 应用中,推荐使用create-react-app。你可以更改import保持代码清晰。



怎么解决 CSS 嵌入 JS 问题?

Next.js 自带styled-jsx库支持 CSS 嵌入 JS。而且你可以选择其他嵌入方法到你的项目中,可参考文档as mentioned before。


哪些语法会被转换?怎么转换它们?

我们遵循 V8 引擎的,如今 V8 引擎广泛支持 ES6 语法以及asyncawait语法,所以我们支持转换它们。但是 V8 引擎不支持修饰器语法,所以我们也不支持转换这语法。

可以参照这些 以及 这些



为什么使用新路由?

Next.js 的特别之处如下所示:

路由不需要被提前知道

路由总是被懒加载

顶层组件可以定义生命周期getInitialProps来阻止路由加载(当服务端渲染或路由懒加载时)

因此,我们可以介绍一个非常简单的路由方法,它由下面两部分组成:

每个顶层组件都将会收到一个url对象,来检查 url 或修改历史记录

组件用于包装如()标签的元素容器,来执行客户端转换。

我们使用了些有趣的场景来测试路由的灵活性,例如,可查看nextgram。



我怎么定义自定义路由?

我们通过请求处理来添加任意 URL 与任意组件之前的映射关系。

在客户端,我们组件有个属性as,可以装饰改变获取到的 URL。



怎么获取数据?

这由你决定。getInitialProps是一个异步函数async(也就是函数将会返回个Promise)。你可以在任意位置获取数据。


我可以使用 GraphQL 吗?

是的! 这里有个例子Apollo.


我可以使用 Redux 吗?

是的! 这里有个例子


我可以在 Next 应用中使用我喜欢的 Javascript 库或工具包吗?

从我们第一次发版就已经提供很多例子,你可以查看这些例子。


什么启发我们做这个?

我们实现的大部分目标都是通过 Guillermo Rauch 的Web 应用的7原则来启发出的。

PHP 的易用性也是个很好的灵感来源,我们觉得 Next.js 可以替代很多需要用 PHP 输出 HTML 的场景。

与 PHP 不同的是,我们得利于 ES6 模块系统,每个文件会输出一个组件或方法,以便可以轻松的导入用于懒加载和测试

我们研究 React 的服务器渲染时并没有花费很大的步骤,因为我们发现一个类似于 Next.js 的产品,React 作者 Jordan Walke 写的react-page (现在已经废弃)


贡献

可查看 contributing.md

作者

Arunoda Susiripala (@arunoda) – ZEIT

Tim Neutkens (@timneutkens) – ZEIT

Naoyuki Kanezawa (@nkzawa) – ZEIT

Tony Kovanen (@tonykovanen) – ZEIT

Guillermo Rauch (@rauchg) – ZEIT

Dan Zajdband (@impronunciable) – Knight-Mozilla / Coral Project

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/97685.html

相关文章

  • nextjs踩坑

    摘要:踩坑几乎一整年没咋写文章,主要是懒,加上工作也挺忙。遇到一些坑,也有一些收获这里记录一下。个人习惯使用启动服务。总结说了上面那么多,其实官方文档里都有相关例子,就当我的个人踩坑记录吧。 Next.js踩坑 几乎一整年没咋写文章,主要是懒,加上工作也挺忙。但是想趁着年底发一篇,希望明年更勤奋一点。其实不是没东西写,就是想深入一个东西还是很困难的,要查各种资料,最终还是懒就是了。 next...

    JayChen 评论0 收藏0
  • 这是一个快速构建nextjs应用的工具

    摘要:官方也有一个的工具,但是只能简单的安装基础的依赖,或者通过他们提供的某个例子来构建自己的项目。由于目前还在开发阶段只支持上面这些的配置也是平常我用的比较多的配置。 因为公司的业务需求现在开发都用nextjs来实现React的服务端渲染,为了之后开发方便自己写了个脚手架工具。 官方也有一个create-next-app的工具,但是只能简单的安装基础的依赖,或者通过他们提供的某个例子来构建...

    scola666 评论0 收藏0
  • Next.js之基础概念(二)

    摘要:样式在中写样式,一般可以归为类,一类是基于文件的传统方式包括,等,另一类则是。我们回到我们的代码中,更改,代码如下在标签中,我们写我们的,必须包含在中,否则会报错。至此,的基础概念已经介绍完了,更高级的用法,可以参考官方的例子。 本篇教程基于上一篇的基础,主要讲解服务端渲染,样式以及部署相关的一些知识,若你没有看过上一篇的内容,或者你看过又忘了,建议重新去看一遍。 顺便说一句,Next...

    DirtyMind 评论0 收藏0
  • Next.js之基础概念

    摘要:是一个基于实现的服务端渲染框架,地址为。本文先从简单地基础概念开始,一步一步带大家认识。本篇教程到此结束,后面会跟大家介绍的服务端渲染及以及部署相关的一下概念及示例代码。 Next.js是一个基于React实现的服务端渲染框架,github地址为next.js。 使用Next.js实现服务端渲染是一件非常简单的事,在这里,你完全可以不用自己去写webpack等配置,Next.js全都帮...

    2bdenny 评论0 收藏0
  • 使用Vue、React和Koa开发个人博客

    摘要:前言实习了大半年,又临近毕业,一直想做一个属于自己的博客。于是就用和开发了一个服务端渲染的博客系统。其中后端管理使用开发前端页面使用服务端渲染框架接口服务使用使用当接口发生时向我发送邮件通知项目地址是点我访问项目地址,不要脸地求。 前言 实习了大半年,又临近毕业,一直想做一个属于自己的博客。于是就用Vue、React和Koa开发了一个服务端渲染的博客系统。其中: 后端管理使用Vue开...

    pf_miles 评论0 收藏0

发表评论

0条评论

luckyw

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<