预渲染

Nuxt 允许页面在构建时静态渲染以提升某些性能或 SEO 指标

Maschinelle Übersetzung ·0 % geprüft

Nuxt 允许将应用中的部分页面在构建时渲染。Nuxt 在请求这些页面时会提供预构建好的页面,而不是在运行时动态生成它们。

Weitere Informationen findest du unter Nuxt 渲染模式.

基于爬虫的预渲染

使用 nuxt generate 命令,通过 Nitro 爬虫构建并预渲染应用。此命令类似于设置 nitro.static 选项为 true 的 nuxt build,或运行 nuxt build --prerender。

这会构建你的网站,启动一个 nuxt 实例,并且默认会预渲染根页面 /,以及该页面链接的任何站点页面、这些页面所链接的页面,依此类推。

npx nuxt generate

你现在可以将 .output/public 目录部署到任意静态托管服务,或使用 npx serve .output/public 在本地预览。

静态构建和预渲染构建还会生成 200.html 和 404.html SPA 回退页面。请参阅什么是 200.html 和 404.html?。

Nitro 爬虫的工作原理:

  1. 加载应用根路由(/)的 HTML、~/pages 目录中的任何非动态页面,以及 prerender.routes 数组中的其他路由。
  2. 将 HTML 和 _payload.json 保存到 ~/.output/public/ 目录,以供静态托管。
  3. 查找 HTML 中的所有锚点标签(<a href="...">),以导航到其他路由。
  4. 对找到的每个锚点标签重复步骤 1-3,直到没有更多锚点标签可供爬取。

理解这一点很重要,因为未被任何可发现页面链接的页面无法被自动预渲染。

详细了解 nuxt generate 命令。

选择性预渲染

你可以在 nuxt.config 文件中手动指定 Nuxt 在构建期间获取并预渲染的路由,或忽略不想预渲染的路由(例如 /dynamic):

export default defineNuxtConfig({
  prerender: {
    routes: ['/user/1', '/user/2'],
    ignore: ['/dynamic'],
  },
})

你可以将此与 crawlLinks 选项结合使用,以预渲染爬虫无法发现的一组路由,例如你的 /sitemap.xml 或 /robots.txt:

export default defineNuxtConfig({
  prerender: {
    crawlLinks: true,
    routes: ['/sitemap.xml', '/robots.txt'],
  },
})

顶层 prerender 选项适用于你使用的任何 server.builder。特定于构建器的选项(例如 Nitro 的 concurrency 或 failOnError)可以在 nitro.prerender 中设置。

在 Nitro 文档中阅读有关预渲染的更多内容。

最后,你也可以使用 routeRules 手动配置此行为。

export default defineNuxtConfig({
  routeRules: {
    // 将 prerender 设置为 true 以配置该路由被预渲染
    '/rss.xml': { prerender: true },
    // 将其设置为 false 以配置该路由在预渲染时被跳过
    '/this-DOES-NOT-get-prerendered': { prerender: false },
    // /blog 下的所有内容都会被预渲染,只要它
    // 能从另一个页面链接到
    '/blog/**': { prerender: true },
  },
})
阅读有关 Nitro 的 routeRules 配置的更多内容。

作为简写,你也可以在页面文件中使用 defineRouteRules 配置此项。

此功能为实验性功能,若要使用,必须在 nuxt.config 中启用 experimental.inlineRouteRules 选项。
app/pages/index.vue
<script setup>
// 或在页面级别设置
defineRouteRules({
  prerender: true,
})
</script>

<template>
  <div>
    <h1>主页</h1>
    <p>在构建时预渲染</p>
  </div>
</template>

这将被转换为:

nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    '/': { prerender: true },
  },
})

负载提取

当 Nuxt 在服务器上渲染页面时,它会将数据获取(useAsyncData 和 useFetch)以及应用状态(useState)的结果序列化到负载中,以便客户端可以在不重新获取数据的情况下进行 hydration。启用负载提取后,Nuxt 还会将此负载写入与路由 HTML 同目录下的 _payload.json 文件中:

  • 预渲染路由会在构建时生成负载文件。
  • 使用 ISR 或 SWR 缓存的路由会在首次渲染路由时生成负载文件,即使是在混合(非静态)站点上也是如此。

在客户端导航期间,Nuxt 会获取目标路由的 _payload.json 文件,并重用提取出的数据,而不是在浏览器中再次运行数据获取。

你可以使用 experimental.payloadExtraction 选项控制此行为:

  • 'client' - 负载会在初始渲染时内联到 HTML 中,并在客户端导航时提取到 _payload.json 文件中。首次加载时不会有额外的网络请求。
  • true - 负载会在初始渲染和客户端导航时都提取到单独的 _payload.json 文件中。HTML 更小,且负载文件可以被 CDN 缓存,但首次加载会多一次请求。
  • false - 禁用负载提取。负载始终内联在 HTML 中,不会生成 _payload.json 文件。

默认值为 true,如果设置了 compatibilityVersion: 5 则默认为 'client'。当设置 ssr: false 时,它会被强制设为 false。

export default defineNuxtConfig({
  experimental: {
    payloadExtraction: 'client',
  },
})

还需要注意以下几个实际影响:

  • 在完全静态站点上,客户端导航会重用构建时捕获的数据,因此在下一次重新构建之前,数据可能是过期的。
  • 对于 ISR/SWR 路由,CDN 可以将负载文件与 HTML 一起缓存,从而提升已缓存路由的客户端导航性能。像 pages/[...slug].vue 这样的动态路由可以通过 /**': { isr: true } 等通配模式选择启用。
  • 负载使用 devalue 序列化,因此自定义类型(例如类实例)需要带有自定义 reducer 和 reviver 的负载插件,才能在往返过程中保持不变。
Weitere Informationen findest du unter 负载 reducer 和 reviver.

运行时预渲染配置

prerenderRoutes

你可以在 Nuxt 上下文中于运行时使用此功能,为 Nitro 添加更多要预渲染的路由。

app/pages/index.vue
<script setup>
prerenderRoutes(['/some/other/url'])
prerenderRoutes('/api/content/article/my-article')
</script>

<template>
  <div>
    <h1>这将在预渲染时为其他路由注册预渲染</h1>
  </div>
</template>
Weitere Informationen findest du unter prerenderRoutes.

prerender:routes Nuxt 钩子

这个钩子在预渲染之前被调用,用于注册额外的路由。

nuxt.config.ts
export default defineNuxtConfig({
  hooks: {
    async 'prerender:routes' (ctx) {
      const { pages } = await fetch('https://api.some-cms.com/pages').then(
        res => res.json(),
      )
      for (const page of pages) {
        ctx.routes.add(`/${page.name}`)
      }
    },
  },
})

prerender:generate Nitro 钩子

该钩子在预渲染过程中对每个路由分别调用。你可以用它对每个被预渲染的路由进行更精细的处理。

nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    hooks: {
      'prerender:generate' (route) {
        if (route.route?.includes('private')) {
          route.skip = true
        }
      },
    },
  },
})