Lzh on GitHub

Rendering Modes

了解 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 文档中,爬虫可以毫无开销地对其进行索引。

哪些是服务器端渲染的,哪些是客户端渲染的?

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

app.vue
<script setup lang="ts">
const counter = ref(0); // executes in server and client environments

const handleClick = () => {
  counter.value++; // executes only in a client environment
};
</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 应用程序的交互性。
  • 搜索引擎优化:通用渲染像传统的服务器应用程序一样,将页面的完整 HTML 内容传递给浏览器。Web 爬虫可以直接索引页面的内容,这使得通用渲染成为您希望快速索引的任何内容的绝佳选择。

服务器端渲染的缺点:

  • 开发约束:服务器和浏览器环境不提供相同的 API,编写可以在两端无缝运行的代码可能很棘手。幸运的是,Nuxt 提供了指南和特定的变量来帮助您确定代码的执行位置。
  • 成本:服务器需要持续运行才能实时渲染页面。这会产生月度的成本,就像任何传统的服务器一样。然而,得益于通用渲染(universal rendering)机制(浏览器在客户端导航时会接管渲染任务),服务器调用量已大幅减少。通过利用 边缘渲染(edge-side-rendering),可进一步降低成本。

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

有关编写没有水合不匹配的 Vue 代码的更多示例,请参阅 Vue 文档

当导入依赖于浏览器 API 并且具有副作用的库时,请确保导入它的组件仅在客户端调用。打包器不会 tree-shake 包含副作用的模块的导入。

客户端渲染

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

客户端渲染的优点:

  • 开发速度:完全在客户端工作时,我们不必担心代码的服务器兼容性,例如,通过使用仅浏览器的 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 文件,其中包含您希望用于渲染加载屏幕的一些 HTML,该加载屏幕将一直渲染到您的应用程序水合完成。
Read more in 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() // Do not generate any routes (except the defaults)
    }
  },
})

这将生成三个文件:

  • 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: {
    // Homepage pre-rendered at build time
    '/': { prerender: true },
    // Products page generated on demand, revalidates in background, cached until API response changes
    '/products': { swr: true },
    // Product pages generated on demand, revalidates in background, cached for 1 hour (3600 seconds)
    '/products/**': { swr: 3600 },
    // Blog posts page generated on demand, revalidates in background, cached on CDN for 1 hour (3600 seconds)
    '/blog': { isr: 3600 },
    // Blog post page generated on demand once until next deployment, cached on CDN
    '/blog/**': { isr: true },
    // Admin dashboard renders only on client-side
    '/admin/**': { ssr: false },
    // Add cors headers on API routes
    '/api/**': { cors: true },
    // Redirects legacy urls
    '/old-page': { redirect: '/new-page' }
  }
})

路由规则

您可以使用的不同属性如下:

  • redirect: string - 定义服务器端重定向。
  • ssr: boolean - 禁用应用程序部分 HTML 的服务器端渲染,并使其仅在浏览器中使用 ssr: false 渲染。
  • cors: boolean - 使用 cors: true 自动添加 cors 标头 - 您可以通过覆盖 headers 来自定义输出。
  • headers: object - 为您网站的各个部分添加特定的标头 - 例如,您的 assets
  • swr: number | boolean - 将缓存标头添加到服务器响应,并在服务器或反向代理上缓存可配置的 TTL(生存时间)。Nitro 的 node-server 预设能够缓存完整的响应。当 TTL 过期时,将发送缓存的响应,同时将在后台重新生成页面。如果使用 true,则会添加一个没有 MaxAge 的 stale-while-revalidate 标头。
  • 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 时,混合渲染不可用。

示例:

Nuxt Vercel ISR

一个使用混合渲染并部署在 Vercel 上的 Nuxt 应用程序示例。

边缘端渲染

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

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

当请求一个页面时,它不会一直到达原始服务器,而是被最近的边缘服务器拦截。该服务器生成页面的 HTML 并将其发送回用户。此过程最大限度地减少了数据必须传输的物理距离,从而 减少了延迟并加快了页面加载速度

边缘端渲染之所以成为可能,要归功于为 Nuxt 提供支持的 服务器引擎 Nitro。它为 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 环境变量。
  • 请注意,在使用带有路由规则的边缘端渲染时,可以使用 混合渲染

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

Nuxt Todos Edge

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

Atinotes

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