译 - Next.js Pages

ShiftWatchOut,前端React

在 Next.js 中,一个 pageReact Component (opens in a new tab) 来自 pages 文件夹下的 .js, .jsx, .ts, 或 .tsx 文件。每一个 page 都基于它的文件名进行路由

:如果你像下面这样新建一个 pages/about.js 并导出 React component,你就可以通过 /about 访问到它.

function About() {
  return <div>About</div>
}

export default About

动态路由的 Pages

Next.js 支持动态路由。例如,你创建一个 pages/posts/[id].js 文件,就可以通过 posts/1posts/2等进行访问。

更多关于动态路由,详见 Dynamic Routing documentation.

预渲染

一般来说,Next.js 预渲染 所有页面。这意味着 Next.js 预先给每一页生成 HTML,而非把任务交给客户端 JS 。预渲染能提供更好的 SEO。

每个生成的页面带有当前页所需最少的 JS 代码。当一个页面被浏览器加载后,它内部的最少代码会使这个页面依然完全可用。(这个过程叫做 hydration。)

两种形式的预渲染

Next.js 有两种形式的预渲染:纯静态生成服务端渲染. The difference is in when it generates the HTML for a page.

重要的是,Next.js 允许你为每一页 选择 你喜欢的那种预渲染。大多数页面通过纯静态生成,部分通过服务端渲染,你可以打造一个 "混合" Next.js 应用

出于性能考虑我们 更推荐 使用 纯静态生成 而非服务端渲染。静态生成的文件可以通过 CDN 缓存,无须任何额外配置即可获得良好性能。但在某些情况下,服务端渲染可能是唯一选择。

你也可以把 客户端渲染 使用到纯静态生成或服务端渲染中。就算是页面中的一部分内容可以由浏览器的 JS 负责渲染。

参考Data Fetching 文档.

纯静态生成 (建议使用)

如果一个页面用到 纯静态生成 技术, 页面 HTML 会在 构建时 生成。在生产环境下, 页面 HTML 是在你运行 next build 时就已经生成. 这些 HTML 会在将来每个请求中被复用,可以被 CDN 缓存起来。

在 Next.js 中, 你可以静态生成 有或没有数据 的页面。让我们来都看一遍。

无数据纯静态生成

Next.js 通过纯静态生成预渲染的页面默认不会自行发起数据请求。例:

function About() {
  return <div>About</div>
}

export default About

注意,这个页面进行预渲染时完全不需要请求任何外部数据。这时,Next.js 在构建过程中为每个 page 生成一个 HTML 单文件。

有数据纯静态生成

有些页面在预渲染时需要拉取数据。现在有两种方案, 可以随意使用。当然每种方案都需要用到 Next.js 提供的专用函数

  1. 页面 内容 依赖外部数据:使用 getStaticProps.
  2. 页面 路径依赖外部数据:使用 getStaticPaths (通常作为 getStaticProps 的补充).

方案 1:页面内容依赖外部数据

:你的博客页面可能需要从一个 CMS (content management system)拉取文章列表

// TODO: Need to fetch `posts` (by calling some API endpoint)
//       before this page can be pre-rendered.
function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  )
}

export default Blog

为了在组件预渲染时拉取数据, Next.js 允许你在同一文件 export 一个名为 getStaticPropsasync 函数。这个函数会在编译时被调用,在预渲染时把拉取的数据传递给页面的 props

function Blog({ posts }) {
  // Render posts...
}

// This function gets called at build time
export async function getStaticProps() {
  // Call an external API endpoint to get posts
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  // By returning { props: { posts } }, the Blog component
  // will receive `posts` as a prop at build time
  return {
    props: {
      posts
    }
  }
}

export default Blog

更多关于 getStaticProps 原理,请看 Data Fetching documentation.

Scenario 2:页面路径依赖外部数据

Next.js 允许你创建带有 动态路由 的页面。例如,你可以新建一个 pages/posts/[id].js 文件来基于 id 变化的博文内容。当你想访问 posts/1 时,你获取到的,就是一篇 id: 1 的文章。

更多关于动态路由的内容,查看Dynamic Routing documentation.

还有一点,你想在构建时预渲染的 id 可能依赖外部数据。

:假设你数据库里现在只有一篇博文 (即 id: 1)。这时,你就只想在构建时预渲染 posts/1

后来,你也许会加上第二篇博文 id: 2。那么你就会想把 posts/2 也纳入预渲染的范畴

也就是说预渲染的页面 路径 依赖外部数据。Next.js 的解决方案是让你从动态 page 文件(例如 pages/posts/[id].js)里export 一个名叫 getStaticPathsasync 函数。这个函数会在构建时调用,它允许你指定想要预渲染的路径。

// This function gets called at build time
export async function getStaticPaths() {
  // Call an external API endpoint to get posts
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  // Get the paths we want to pre-render based on posts
  const paths = posts.map((post) => ({
    params: { id: post.id }
  }))

  // We'll pre-render only these paths at build time.
  // { fallback: false } means other routes should 404.
  return { paths, fallback: false }
}

当然在 pages/posts/[id].js 里, 你仍需要导出 getStaticProps 这样你才能获取到与这个 id 相绑定的数据,并将其预渲染到页面上:

function Post({ post }) {
  // Render post...
}

export async function getStaticPaths() {
  // ...
}

// This also gets called at build time
export async function getStaticProps({ params }) {
  // params contains the post `id`.
  // If the route is like /posts/1, then params.id is 1
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()

  // Pass post data to 页面 via props
  return { props: { post } }
}

export default Post

更多关于 getStaticPaths 原理,请看 Data Fetching documentation.

何时使用纯静态生成?

我们推荐尽可能地使用 纯静态生成 (无论有无拉取数据的需要),这样页面可以一次性构建完成,通过 CDN 分发,这样就比每次请求时都由服务器渲染一次要快得多。

你可以纯静态生成各种页面,包括:

需要考虑清楚:"能否在用户请求 之前 就预渲染这个页面?"结果肯定的话,就应当使用纯静态生成.

换言之,纯静态生成就 适用于你无法在用户请求之前就预渲染页面的场景. 或许页面展示的是需要频繁更新的数据,或者页面内容在每次请求后都要变化.

这种情境下,可以采用以下的方案:

服务端渲染

也即 "SSR" 或 "动态渲染"。

如果一个页面使用 服务端渲染, 页面 HTML 是在 每次请求时 生成的。

要将一个页面变成服务端渲染,你需要 export 一个 叫getServerSidePropsasync 函数。这个函数会在每次请求时由服务器执行。

例如,假设页面需要预渲染经常更新的数据(从外部 API 拉取)。你可以像下面这样编写 getServerSideProps 来获取数据并把它传给 Page

function Page({ data }) {
  // Render data...
}

// This gets called on every request
export async function getServerSideProps() {
  // Fetch data from external API
  const res = await fetch(`https://.../data`)
  const data = await res.json()

  // Pass data to the page via props
  return { props: { data } }
}

export default Page

如你所见,getServerSideProps 很像 getStaticProps, 但它俩的不同之处在于 getServerSideProps 是运行在每次请求时,而非构建时。

了解更多 getServerSideProps 原理,请看Data Fetching documentation

总结

我们讨论了两种形式的预渲染 for Next.js.

© ShiftWatchOut.RSS