渲染模式
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>计数: {{ counter }}</p>
<button @click="handleClick">增加</button>
</div>
</template>
在初始请求时,counter
ref 在服务端初始化,因为它在 <p>
标签中渲染。handleClick
的内容在此处从不执行。在浏览器中进行水合时,counter
ref 会重新初始化。handleClick
最终将自身绑定到按钮;因此可以合理推断 handleClick
的主体始终在浏览器环境中运行。
中间件和页面在服务端和客户端的水合期间运行。插件可以在服务端、客户端或两者上渲染。组件也可以强制仅在客户端运行。组合式函数和工具函数根据其使用上下文进行渲染。
服务端渲染的优点:
- 性能:用户可以立即访问页面内容,因为浏览器显示静态内容的速度远快于 JavaScript 生成的内容。同时,Nuxt 在水合过程中保留了 Web 应用的交互性。
- 搜索引擎优化:通用渲染像传统服务端应用一样向浏览器传递完整的 HTML 内容。网络爬虫可以直接索引页面内容,这使得通用渲染成为快速索引任何内容的理想选择。
服务端渲染的缺点:
- 开发限制:服务端和浏览器环境提供的 API 不同,编写能够无缝运行在两端的代码可能具有挑战性。幸运的是,Nuxt 提供了指南和特定变量来帮助您确定代码的执行位置。
- 成本:需要运行一个服务端来动态渲染页面,这会增加每月成本,就像任何传统服务端一样。然而,由于浏览器接管客户端导航,通用渲染显著减少了服务端调用。通过利用边缘渲染可以进一步降低成本。
通用渲染非常多功能,几乎适用于任何用例,特别适合内容导向的网站:博客、营销网站、作品集、电子商务网站和市场。
客户端渲染
默认情况下,传统的 Vue.js 应用在浏览器(或客户端)中渲染。然后,Vue.js 在浏览器下载并解析包含创建当前界面指令的所有 JavaScript 代码后生成 HTML 元素。
客户端渲染的优点:
- 开发速度:完全在客户端工作时,我们无需担心代码的服务端兼容性,例如使用仅限浏览器的 API(如
window
对象)。 - 成本较低:运行服务端会增加基础设施成本,因为您需要在支持 JavaScript 的平台上运行。仅客户端应用可以托管在任何静态服务器上,仅需 HTML、CSS 和 JavaScript 文件。
- 离线支持:由于代码完全在浏览器中运行,它可以在无网络连接时正常工作。
客户端渲染的缺点:
- 性能:用户必须等待浏览器下载、解析和运行 JavaScript 文件。根据网络下载速度和用户设备的解析与执行能力,这可能需要一些时间并影响用户体验。
- 搜索引擎优化:通过客户端渲染传递的内容需要更长时间进行索引和更新,与服务端渲染的 HTML 文档相比。这与性能缺点有关,因为搜索引擎爬虫在第一次尝试索引页面时不会等待界面完全渲染。使用纯客户端渲染,您的内容在搜索结果页面中显示和更新的时间会更长。
客户端渲染适合需要大量交互的 Web 应用,这些应用不需要索引或用户频繁访问。它可以利用浏览器缓存跳过后续访问的下载阶段,例如 SaaS、后台管理应用或在线游戏。
您可以在 nuxt.config.ts
中启用仅客户端渲染:
export default defineNuxtConfig({
ssr: false
})
ssr: false
,您还应在 ~/spa-loading-template.html
中放置一个 HTML 文件,包含您希望用于渲染加载屏幕的 HTML,该屏幕将在应用水合完成之前显示。部署静态客户端渲染应用
如果您使用 nuxt generate
或 nuxt build --prerender
命令将应用部署到静态托管,默认情况下,Nuxt 将为每个页面生成单独的静态 HTML 文件。
nuxt generate
或 nuxt build --prerender
命令预渲染应用,您将无法使用任何服务端端点,因为输出文件夹中不包含服务端。如果您需要服务端功能,请使用 nuxt build
。如果您使用纯客户端渲染,这可能是不必要的。您可能只需要一个 index.html
文件,以及 200.html
和 404.html
作为后备,您可以告知静态 Web 主机为所有请求提供这些文件。
为了实现这一点,我们可以更改路由的预渲染方式。只需在 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 路由组定义规则,更改渲染模式或根据路由分配缓存策略!
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 路由上添加 CORS 头
'/api/**': { cors: true },
// 重定向旧页面
'/old-page': { redirect: '/new-page' }
}
})
路由规则
您可以使用的不同属性如下:
redirect: string
- 定义服务端重定向。ssr: boolean
- 禁用应用的某些部分的 HTML 服务端渲染,并使用ssr: false
使其仅在浏览器中渲染。cors: boolean
- 使用cors: true
自动添加 CORS 头 - 您可以通过headers
自定义输出。headers: object
- 为网站的某些部分添加特定头,例如您的资产。swr: number | boolean
- 为服务器响应添加缓存头并在服务器或反向代理上缓存,设置可配置的 TTL(生存时间)。Nitro 的node-server
预设能够缓存完整响应。当 TTL 过期时,将发送缓存的响应,同时页面将在后台重新生成。如果使用true
,将添加stale-while-revalidate
头而不设置 MaxAge。isr: number | boolean
- 行为与swr
相同,但我们能够将响应添加到支持此功能的平台(当前为 Netlify 或 Vercel)的 CDN 缓存中。如果使用true
,内容将在 CDN 中持续存在直到下次部署。prerender: boolean
- 在构建时预渲染路由并将其作为静态资产包含在构建中。noScripts: boolean
- 禁用网站某些部分的 Nuxt 脚本和 JS 资源提示的渲染。appMiddleware: string | string[] | Record<string, boolean>
- 允许您定义应用的 Vue 部分(即非 Nitro 路由)中页面路径应运行或不应运行的中间件。
只要可能,路由规则将自动应用于部署平台的本地规则以获得最佳性能(当前支持 Netlify 和 Vercel)。
nuxt generate
时混合渲染不可用。示例:
icon: i-simple-icons-github title: Nuxt Vercel ISR to: https://github.com/danielroe/nuxt-vercel-isr target: _blank ui.icon.base: text-black dark:text-white
在 Vercel 上部署的具有混合渲染的 Nuxt 应用示例。
边缘渲染
边缘渲染(ESR)是 Nuxt 引入的一项强大功能,允许通过内容分发网络(CDN)的边缘服务器在靠近用户的位置渲染您的 Nuxt 应用。通过利用 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
环境变量
请注意,使用边缘渲染时可以使用混合渲染和路由规则。
您可以探索在上述平台上部署的开源示例:
icon: i-simple-icons-github title: Nuxt Todos Edge to: https://github.com/atinux/nuxt-todos-edge target: _blank ui.icon.base: text-black dark:text-white
一个具有用户认证、SSR 和 SQLite 的待办事项应用。
icon: i-simple-icons-github title: Atinotes to: https://github.com/atinux/atinotes target: _blank ui.icon.base: text-black dark:text-white
基于 Cloudflare KV 的具有通用渲染的可编辑网站。