预渲染
Nuxt 允许在构建时静态渲染页面,以优化性能或 SEO 指标。
Nuxt 允许在构建时预渲染应用中的指定页面。请求时 Nuxt 会直接返回预构建的页面,而不是动态生成。
基于爬虫的预渲染
使用 nuxt generate 命令 来构建和预渲染应用,它使用 Nitro 爬虫。这个命令等同于 nuxt build 设置 nitro.static 为 true,或者运行 nuxt build --prerender。
它会构建站点、启动一个 Nuxt 实例,默认预渲染根页面 /,以及站点中链接到的所有页面、这些页面再链接到的页面,依此类推。
npx nuxt generate
yarn nuxt generate
pnpm nuxt generate
bun x nuxt generate
deno x nuxt generate
现在你可以把 .output/public 目录部署到任何静态托管服务,或者本地用 npx serve .output/public 预览。
静态和预渲染构建还会生成 200.html 和 404.html SPA 兜底页面。参考什么是 200.html 和 404.html?。
Nitro 爬虫的工作方式:
- 加载应用根路由(
/)的 HTML、~/pages目录中的所有非动态页面,以及prerender.routes数组中的其他路由。 - 将 HTML 和
_payload.json保存到~/.output/public/目录,用于静态服务。 - 在 HTML 中找到所有锚标签(
<a href="...">),导航到其他路由。 - 对每个找到的锚标签重复步骤 1-3,直到没有更多可爬的锚标签。
理解这一点很重要,因为没有被可发现页面链接到的页面无法自动预渲染。
阅读更多关于 nuxt generate 命令的内容。
选择性预渲染
你可以在 nuxt.config 文件中手动指定需要预渲染的路由,或者忽略不想预渲染的路由(比如 /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 选项。
<script setup>
// 或者在页面级别设置
defineRouteRules({
prerender: true,
})
</script>
<template>
<div>
<h1>首页</h1>
<p>构建时预渲染</p>
</div>
</template>
这会被转换为:
export default defineNuxtConfig({
routeRules: {
'/': { prerender: true },
},
})
Payload 提取
当 Nuxt 在服务端渲染页面时,它会将数据获取结果(useAsyncData 和 useFetch)和应用状态(useState)序列化为 payload,这样客户端水合时就不需要重新获取。启用 payload 提取后,Nuxt 还会将这个 payload 写入路由 HTML 旁边的 _payload.json 文件:
- 预渲染的路由在构建时生成 payload 文件。
- 使用 ISR 或 SWR 缓存的路由在第一次渲染时生成 payload 文件,即使是在混合(非静态)站点上。
客户端导航时,Nuxt 会获取目标路由的 _payload.json 文件,复用提取的数据,而不是在浏览器中重新执行数据获取。
你可以通过 experimental.payloadExtraction 选项控制这个行为:
'client'- payload 内联在 HTML 中用于初始渲染,客户端导航时提取为_payload.json文件。首次加载没有额外网络请求。true- 初始渲染和客户端导航都将 payload 提取到单独的_payload.json文件。HTML 更小,payload 文件可以被 CDN 缓存,代价是首次加载多一个请求。false- 禁用 payload 提取。payload 始终内联在 HTML 中,不生成_payload.json文件。
默认是 true,设置 compatibilityVersion: 5 时为 'client'。设置 ssr: false 时强制为 false。
export default defineNuxtConfig({
experimental: {
payloadExtraction: 'client',
},
})
需要注意几个实际影响:
- 在完全静态的站点上,客户端导航复用构建时捕获的数据,所以在下次重新构建之前数据可能是过期的。
- 对于 ISR/SWR 路由,CDN 可以缓存 payload 文件和 HTML 一起,提升缓存路由的客户端导航性能。像
pages/[...slug].vue这样的动态路由可以用 glob 模式 opt-in,比如'/**': { isr: true }。 - Payload 使用 devalue 序列化,所以自定义类型(比如类实例)需要 payload 插件配合自定义 reducer 和 reviver 才能正确往返。
运行时预渲染配置
prerenderRoutes
你可以在 Nuxt 上下文中运行时调用它,添加更多需要 Nitro 预渲染的路由。
<script setup>
prerenderRoutes(['/some/other/url'])
prerenderRoutes('/api/content/article/my-article')
</script>
<template>
<div>
<h1>预渲染时会注册其他路由</h1>
</div>
</template>
prerender:routes Nuxt hook
在预渲染之前调用,用于注册更多路由。
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 hook
预渲染过程中每个路由都会调用。你可以用它来精细处理每个预渲染的路由。
export default defineNuxtConfig({
nitro: {
hooks: {
'prerender:generate' (route) {
if (route.route?.includes('private')) {
route.skip = true
}
},
},
},
})