渲染模式
Nuxt 支持不同的渲染模式,包含通用渲染、客户端渲染,同时还提供混合渲染及在CDN 边缘服务器渲染应用的可能性。
浏览器和服务器都可以解释 JavaScript 代码,将 Vue.js 组件转化为 HTML 元素。这个步骤称为 渲染。Nuxt 支持 通用 和 客户端 渲染,这两种方法各有优缺点,我们将在下文进行介绍。
默认情况下,Nuxt 使用 通用渲染 来提升用户体验、性能并优化搜索引擎索引,但你可以通过一行配置切换渲染模式。
通用渲染
这一步类似于传统的由 PHP 或 Ruby 应用执行的 服务器端渲染。当浏览器请求开启了通用渲染的 URL 时,Nuxt 会在服务器环境中运行 JavaScript(Vue.js)代码,并返回一个完整渲染好的 HTML 页面给浏览器。Nuxt 也可能从缓存中返回预先生成的完整 HTML 页。用户会立即获得应用初始内容的全部,相较于客户端渲染更快。
HTML 文档下载完毕后,浏览器会解析它,然后 Vue.js 会接管该文档。之前服务器端运行的相同 JavaScript 代码现在重新在客户端(浏览器)后台执行,使页面启用交互性(这就是 通用渲染)并将监听器绑定到 HTML 元素。这一步称为 激活(Hydration)。激活完成后,页面即可享有动态界面和页面过渡等优势。
通用渲染允许 Nuxt 应用实现快速页面加载,同时保留客户端渲染的好处。此外,由于内容已经存在于 HTML 文档中,爬虫可以无需额外开销直接索引内容。
哪些部分是服务器渲染,哪些是客户端渲染?
在通用渲染模式下,常有人会问 Vue 文件的哪些部分在服务器和/或客户端执行。
<script setup lang="ts">
const counter = ref(0); // 在服务器和客户端环境都执行
const handleClick = () => {
counter.value++; // 仅在客户端环境执行
};
</script>
<template>
<div>
<p>Count: {{ counter }}</p>
<button @click="handleClick">Increment</button>
</div>
</template>
在初次请求时,counter
的 ref 因为在 <p>
标签内渲染而在服务器中初始化,handleClick
中的代码则不会被执行。浏览器激活阶段时,counter
ref 会重新初始化,handleClick
函数最终绑定到按钮上。因此可以合理推断,handleClick
的主体始终在浏览器环境运行。
中间件 和 页面 会在服务器和客户端激活阶段运行。插件 可能在服务器、客户端或两者运行。组件 也可以被强制只在客户端运行。组合函数 和 工具函数 会根据使用上下文决定运行环境。
服务器端渲染的优点:
- 性能:用户可以立即访问页面内容,因为浏览器可以比 JavaScript 生成内容更快地显示静态内容。同时,Nuxt 在激活过程中保持了 web 应用的交互性。
- 搜索引擎优化(SEO):通用渲染将页面完整 HTML 内容像经典服务器应用一样交付给浏览器。网络爬虫能直接索引页面内容,这使通用渲染成为希望快速被索引内容的理想选择。
服务器端渲染的缺点:
- 开发限制:服务器和浏览器环境 API 不同,编写能无缝运行于两者的代码会有挑战。幸好,Nuxt 提供了指导和特定变量帮助判断代码执行环境。
- 成本:需要一台服务器实时渲染页面,这会产生类似传统服务器的一定月度费用。不过因为浏览器在客户端导航时承担了相关工作,服务器请求被大大减少。通过边缘渲染还可进一步降低成本。
通用渲染非常灵活,几乎适用于所有应用场景,特别适合内容导向型网站:博客、营销网站、作品集、电商及市场平台。
客户端渲染
传统的 Vue.js 应用默认在浏览器(即客户端)中渲染。浏览器下载并解析包含当前界面构造指令的所有 JavaScript 代码后,Vue.js 才开始生成 HTML 元素。
客户端渲染的优点:
- 开发速度:完全客户端开发时,无需担心代码的服务器兼容性问题,例如可以直接使用只运行在浏览器的 API,如
window
对象。 - 成本较低:不需要运行服务器增加基础设施成本。客户端应用可以托管在任何静态服务器,只需提供 HTML、CSS 和 JavaScript 文件。
- 脱机可用:代码完全运行在浏览器,断网时仍然可以正常工作。
客户端渲染的缺点:
- 性能:用户必须等待浏览器下载、解析和执行 JavaScript 文件。下载依赖网络状况,解析和执行受用户设备影响,这可能增加等待时间,影响用户体验。
- 搜索引擎优化:客户端渲染内容的索引和更新速度比服务器渲染 HTML 慢。搜索引擎爬虫通常不会等待页面完全渲染后再进行索引,因此页面内容显示和更新会延迟。
客户端渲染适合交互性强且不需索引或用户频繁访问的Web 应用。可利用浏览器缓存跳过后续访问的下载阶段,例如 SaaS、后台管理系统或在线游戏。
可在 nuxt.config.ts
中通过设置启用仅客户端渲染:
export default defineNuxtConfig({
ssr: false
})
ssr: false
,应在 ~/app/spa-loading-template.html
中放置一个 HTML 文件,用于加载中页面,直到应用激活完成时显示。部署静态客户端渲染应用
如果使用 nuxt generate
或 nuxt build --prerender
命令部署至静态托管,默认情况下,Nuxt 会将每个页面渲染为独立的静态 HTML 文件。
nuxt generate
或 nuxt build --prerender
命令预渲染应用,则无法使用任何服务器端点,因为输出目录不会包含服务器代码。如需服务器功能,请使用 nuxt build
。纯客户端渲染时,通常只需一个 index.html
文件,再加上 200.html
和 404.html
作为回退文件,静态网站托管服务可针对所有请求返回它们。
可通过更改路由预渲染方式实现。只需在 nuxt.config.ts
的钩子中添加:
export default defineNuxtConfig({
hooks: {
'prerender:routes' ({ routes }) {
routes.clear() // 不生成路由(除了默认路由)
}
},
})
这样将生成三个文件:
index.html
200.html
404.html
根据你使用的托管商,200.html
和 404.html
可能会有用。
跳过客户端回退页面生成
预渲染客户端渲染应用时,Nuxt 默认生成 index.html
、200.html
和 404.html
。如果需要阻止生成这些文件,可以使用 Nitro 的 'prerender:generate'
钩子。
export default defineNuxtConfig({
ssr: false,
nitro: {
hooks: {
'prerender:generate'(route) {
const routesToSkip = ['/index.html', '/200.html', '/404.html']
if (routesToSkip.includes(route.route)) {
route.skip = true
}
}
}
}
})
混合渲染
混合渲染允许针对不同路由使用不同的缓存策略,借助 路由规则 决定服务器如何响应特定 URL 的请求。
过去,Nuxt 应用的每个路由/页面服务器必须使用统一的渲染模式——通用或客户端渲染。在某些场景下,有些页面可以在构建时生成,而其它页面需客户端渲染。例如,一个内容网站包含管理后台,每个内容页应静态生成一次,但管理后台页面由于需要注册,更像动态应用。
Nuxt 支持路由规则和混合渲染。通过路由规则可以为某组路由定义规则,改变渲染模式或指定缓存策略!
Nuxt 服务器会自动注册对应的中间件,并利用 Nitro 缓存层包裹路由的缓存处理。
export default defineNuxtConfig({
routeRules: {
// 首页于构建时预渲染
'/': { prerender: true },
// 产品页面按需生成,后台重新校验,缓存直到 API 响应变化
'/products': { swr: true },
// 产品详情页按需生成,后台重新校验,缓存 1 小时(3600 秒)
'/products/**': { swr: 3600 },
// 博客首页按需生成,后台重新校验,CDN 缓存 1 小时(3600 秒)
'/blog': { isr: 3600 },
// 博客文章页按需生成一次,直到下一次部署,CDN 缓存
'/blog/**': { isr: true },
// 管理后台仅客户端渲染
'/admin/**': { ssr: false },
// API 路由加上跨域请求头
'/api/**': { cors: true },
// 旧页面重定向
'/old-page': { redirect: '/new-page' }
}
})
路由规则
可用属性如下:
redirect: string
- 定义服务器端重定向。ssr: boolean
- 禁用部分应用区域的服务器端渲染,仅在浏览器渲染(ssr: false
)。cors: boolean
- 自动添加 CORS 请求头(cors: true
),可用headers
自定义。headers: object
- 为应用区域添加自定义请求头,例如静态资源。swr: number | boolean
- 添加缓存响应头,在服务器或反向代理端缓存一段 TTL(存活时间)。Nitro 的node-server
预设支持完整响应缓存。TTL 过期后,将返回缓存内容,同时后台重新生成。当为true
时,添加stale-while-revalidate
头且无 MaxAge。isr: number | boolean
- 与swr
功能类似,同时支持 CDN 缓存(目前仅 Netlify 和 Vercel)。为true
时,内容在 CDN 中缓存直到下一次部署。prerender: boolean
- 在构建时预渲染路由,包含为静态资源。noScripts: boolean
- 禁止渲染 Nuxt 脚本和 JS 资源提示。appMiddleware: string | string[] | Record<string, boolean>
- 指定哪些中间件应在 Vue 应用部分运行(不包括 Nitro 路由)。
路由规则会优先自动应用到部署平台本地规则,以实现最佳性能(目前支持 Netlify 和 Vercel)。
nuxt generate
时不可用。示例:
边缘渲染
边缘渲染(Edge-Side Rendering,ESR)是 Nuxt 中引入的强大特性,使你的 Nuxt 应用可通过内容分发网络(CDN)边缘服务器更靠近用户进行渲染。借助 ESR,可以提升性能并减少延迟,提供更优体验。
ESR 将渲染过程推送到网络“边缘”——CDN 的边缘服务器。请注意,ESR 更像是一个部署目标,而非真正的渲染模式。
当对某页面发出请求时,最近的边缘服务器会拦截请求,生成该页面的 HTML 并返回给用户。此过程缩短了数据实际传输距离,降低延迟并更快加载页面。
边缘渲染得益于 Nitro,Nuxt 的服务器引擎,支持跨平台 Node.js、Deno、Cloudflare Workers 等。
当前支持可运用 ESR 的平台包括:
- Cloudflare Pages —— 使用 git 集成及
nuxt build
命令零配置支持。 - Vercel Edge Functions —— 通过
nuxt build
命令及环境变量NITRO_PRESET=vercel-edge
启用。 - Netlify Edge Functions —— 通过
nuxt build
命令及环境变量NITRO_PRESET=netlify-edge
启用。
请注意,使用边缘渲染时,可结合路由规则启用混合渲染。
以下开源示例项目部署在上述部分平台: