渲染模式

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

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

服务端渲染的优点:

  • 性能:用户可以立即访问页面内容,因为浏览器显示静态内容的速度远快于 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.config.ts
export default defineNuxtConfig({
  ssr: false
})
如果使用 ssr: false,你还应该在 ~/app/spa-loading-template.html 中放置一个 HTML 文件,包含你希望用于渲染加载屏幕的 HTML,该屏幕将在应用水合完成前显示。
阅读更多 SPA 加载模板.

部署静态客户端渲染应用

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

如果你使用 nuxi generatenuxi build --prerender 预渲染应用,你将无法使用任何服务器端点,因为输出文件夹中不包含服务器。如果你需要服务器功能,请使用 nuxi build

如果仅使用客户端渲染,这可能是不必要的。你可能只需要一个 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 对于你使用的托管提供商可能有用。

跳过客户端回退生成

在预渲染客户端渲染应用时,Nuxt 默认会生成 index.html200.html404.html 文件。然而,如果你需要防止生成其中部分或全部文件,可以使用 Nitro'prerender:generate' 钩子。

nuxt.config.ts
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 缓存层 包装带有缓存处理程序的路由。

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 },
    // 重定向旧 URL
    '/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 的平台包括:

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

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


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 的可编辑网站,支持通用渲染。