渲染模式

了解 Nuxt 中可用的不同渲染模式。

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 文档中,爬虫可以在没有额外负担的情况下进行索引。

用户在 HTML 文档加载时可以访问静态内容。水合后使页面具有互动性

什么是服务器渲染,什么是客户端渲染?

在通用渲染模式下,询问 Vue 文件的哪些部分在服务器和/或客户端上运行是很正常的。

app.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 的主体将始终在浏览器环境中运行。

中间件页面在水合期间在服务器和客户端运行。插件可以在服务器、客户端或两者上进行渲染。组件也可以强制仅在客户端上运行。组合式 API实用工具则是根据其使用上下文进行渲染。

服务器端渲染的优点:

  • 性能:用户可以立即访问页面内容,因为浏览器显示静态内容的速度要比 JavaScript 生成的内容快得多。同时,Nuxt 在水合过程中保持了 Web 应用程序的互动性。
  • 搜索引擎优化:通用渲染将页面的整个 HTML 内容作为经典服务器应用程序交付给浏览器。网络爬虫可以直接索引页面的内容,这使得通用渲染成为快速索引任何内容的绝佳选择。

服务器端渲染的缺点:

  • 开发限制: 服务器和浏览器环境不提供相同的 API,编写可以无缝运行于双方的代码可能会很棘手。幸运的是,Nuxt 提供了指导和特定变量,帮助您确定代码执行的位置。
  • 成本: 为了动态渲染页面,需要运行一个服务器。这增加了每月的开销,类似于任何传统服务器。不过,由于浏览器在客户端导航中接管,服务器调用的数量显著减少。通过利用边缘侧渲染可以降低成本。

通用渲染非常灵活,可以适应几乎任何用例,特别适合面向内容的网站:博客、营销网站、作品集、电子商务网站和市场。

有关编写 Vue 代码而不产生水合不匹配的更多示例,请参见Vue 文档
在导入依赖于浏览器 API 并具有副作用的库时,请确保引入组件仅在客户端调用。打包工具不会树摇包含副作用的模块的导入。

客户端渲染

开箱即用时,传统的 Vue.js 应用程序在浏览器(或 客户端)中渲染。然后,Vue.js 带着所有的 JavaScript 代码生成 HTML 元素,包含创建当前界面的指令,这些代码会在浏览器下载和解析后运行。

用户必须等到浏览器下载、解析和执行 JavaScript 才能看到页面内容

客户端渲染的优点:

  • 开发速度:在完全在客户端工作时,我们不需要担心代码的服务器兼容性,例如,通过使用仅限浏览器的 API,如 window 对象。
  • 更便宜:运行服务器会增加基础设施的成本,因为您需要在支持 JavaScript 的平台上运行。我们可以在任何静态服务器上托管仅客户端的应用程序,包含 HTML、CSS 和 JavaScript 文件。
  • 离线: 因为代码完全在浏览器中运行,它在互联网不可用时也能正常工作。

客户端渲染的缺点:

  • 性能:用户必须等待浏览器下载、解析和运行 JavaScript 文件。根据网络情况和用户设备的解析与执行,这可能需要一些时间,并影响用户体验。
  • 搜索引擎优化:通过客户端渲染传递的内容的索引和更新时间比服务器渲染的 HTML 文档要长。这和我们讨论的性能缺陷有关,因为搜索引擎爬虫不会等待其首次尝试在页面完全渲染时就进行索引。在纯客户端渲染中,您的内容将需要更多时间显示和更新在搜索结果页面中。

客户端渲染是一个很好的选择,适用于需要高度互动的 Web 应用程序,这些应用程序不需要索引,或者其用户频繁访问。它可以利用浏览器缓存,在后续访问时跳过下载阶段,如 SaaS、后台办公室应用程序或在线游戏

您可以在 nuxt.config.ts 中使用 Nuxt 启用仅客户端的渲染:

nuxt.config.ts
export default defineNuxtConfig({
  ssr: false
})
如果您确实使用 ssr: false,您还应该在 ~/app/spa-loading-template.html 中放置一个包含您想用于渲染加载屏幕的 HTML 文件,直到您的应用程序得到水合。
Read more in SPA 加载模板.
观看 Alexander Lichter 关于 使用 Nuxt 构建简单 SPA!? 的视频。

部署静态客户端渲染应用

如果您使用 nuxi generatenuxi build --prerender 命令将应用部署到静态托管,则默认情况下,Nuxt 将每个页面渲染为单独的静态 HTML 文件。

如果您使用纯客户端渲染,那么这可能是多余的。您可能只需要一个 index.html 文件,以及 200.html404.html 后备文件,可以告诉静态 Web 主机为所有请求提供这些文件。

为了实现这一点,我们可以改变路由的预渲染方式。只需在 nuxt.config.ts 中将其添加到您的钩子中:

nuxt.config.ts
export default 
defineNuxtConfig
({
hooks
: {
'prerender:routes' ({
routes
}) {
routes
.
clear
() // 不生成任何路由(除了默认的)
} }, })

这将生成三个文件:

  • index.html
  • 200.html
  • 404.html

200.html404.html 可能对您使用的托管提供商有用。

混合渲染

混合渲染允许为每个路由使用不同的缓存规则,使用 路由规则,并决定服务器应该如何响应对特定 URL 的新请求。

之前,Nuxt 应用和服务器的每个路由/页面必须使用相同的渲染模式,无论是通用的还是客户端的。在各种情况下,一些页面可以在构建时生成,而其他页面则应客户端渲染。例如,想象一个内容网站具有管理部分。每个内容页面应该主要是静态的并且只生成一次,但管理部分需要注册,并且更像是动态应用。

Nuxt 包含路由规则和混合渲染支持。通过使用路由规则,您可以为一组 nuxt 路由定义规则,根据路由更改渲染模式或分配缓存策略!

Nuxt 服务器将自动注册相应的中间件,并使用Nitro 缓存层包装路由。

nuxt.config.ts
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 相同,只是我们能够将响应添加到支持此功能的平台的 CDN 缓存中(目前为 Netlify 或 Vercel)。如果使用 true,内容将在下次部署之前保留在 CDN 中。
  • prerender: boolean - 在构建时预渲染路由并将其作为静态资源包含在构建中
  • experimentalNoScripts: boolean - 禁用该网站部分的 Nuxt 脚本和 JS 资源提示的渲染。
  • appMiddleware: string | string[] | Record<string, boolean> - 允许您为应用程序 Vue 应用的页面路径定义应运行或不运行的中间件(即,非 Nitro 路由)

在可能的情况下,路由规则将自动应用于部署平台的原生规则,以获得最佳性能(目前支持 Netlify 和 Vercel)。

请注意,使用 nuxt generate 时不支持混合渲染。

示例:

Nuxt Vercel ISR

在 Vercel 上部署的具有混合渲染的 Nuxt 应用示例。

边缘侧渲染

边缘侧渲染(ESR)是 Nuxt 中引入的一项强大功能,允许您通过内容分发网络(CDN)的边缘服务器将 Nuxt 应用程序渲染得更接近用户。通过利用 ESR,您可以确保提高性能和减少延迟,从而提供增强的用户体验。

通过 ESR,渲染过程被推送到网络的“边缘” - CDN 的边缘服务器。请注意,ESR 更像是一个部署目标,而不是一个实际的渲染模式。

当请求页面时,而不是走到原始服务器,它会被最近的边缘服务器拦截。该服务器生成该页面的 HTML 并将其发回给用户。此过程最小化了数据传输的物理距离,减少延迟并更快加载页面

边缘侧渲染得益于Nitro,这是支持 Nuxt 3 的服务器引擎。它为 Node.js、Deno、Cloudflare Workers 等提供跨平台支持。

您可以利用 ESR 的当前平台包括:

请注意,混合渲染可以在使用边缘侧渲染的路由规则时使用。

您可以探索在上述某些平台上部署的开源示例:

Nuxt Todos Edge

一个具有用户身份验证、SSR 和 SQLite 的待办事项应用。

Atinotes

一个基于 Cloudflare KV 的可编辑网站,具有通用渲染。