跳到主要内容

渲染模式

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

Nuxt 支持不同的渲染模式:通用渲染(universal rendering)、客户端渲染(client-side rendering),同时还提供了混合渲染(hybrid-rendering),以及将你的应用渲染在 CDN 边缘服务器(edge-side-rendering)上的可能性。

浏览器和服务端都可以解释 JavaScript 代码,将 Vue.js 组件转换为 HTML 元素。这一步称为渲染。Nuxt 同时支持通用渲染和客户端渲染。这两种方法各有利弊,我们会在下文介绍。

默认情况下,Nuxt 使用通用渲染,以提供更好的用户体验、性能,并优化搜索引擎索引;但你可以通过一行配置切换渲染模式。

通用渲染(Universal Rendering)

这一步类似于传统由 PHP 或 Ruby 应用执行的服务端渲染。当浏览器请求一个启用了通用渲染的 URL 时,Nuxt 会在服务端环境中运行 JavaScript(Vue.js)代码,并将一个完全渲染好的 HTML 页面返回给浏览器。如果该页面是提前生成的,Nuxt 也可能从缓存中返回一个完全渲染好的 HTML 页面。与客户端渲染相反,用户可以立即获取到应用的初始内容的全部内容。

一旦下载了 HTML 文档,浏览器会对其进行解释,然后 Vue.js 接管该文档。先前在服务端运行的同一份 JavaScript 代码会再次在客户端(浏览器)中于后台运行,通过将监听器绑定到 HTML 上,从而启用交互能力(这就是通用渲染的由来)。这被称为水合(Hydration)。水合完成后,页面就能享受诸如动态界面和页面过渡等优势。

通用渲染让 Nuxt 应用能够提供快速的页面加载时间,同时保留客户端渲染的好处。此外,由于内容已经存在于 HTML 文档中,爬虫无需额外开销即可对其进行索引。

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

在通用渲染模式下,自然会有人问:一个 Vue 文件的哪些部分运行在服务端、哪些部分运行在客户端(或两者皆有)。

app/app.vue
<script setup lang="ts">
const counter = ref(0) // 在服务端和客户端环境中都执行

const handleClick = () => {
  counter.value++ // 仅在客户端环境中执行
}
</script>

<template>
  <div>
    <p>Count: {{ counter }}</p>
    <button @click="handleClick">
      增加
    </button>
  </div>
</template>

在初始请求时,由于 counter ref 被渲染在 <p> 标签内,它会在服务端初始化。handleClick 的函数体永远不会在这里执行。在浏览器的水合过程中,counter ref 被重新初始化。handleClick 最终才绑定到按钮上;因此可以合理推断 handleClick 的函数体将始终在浏览器环境中运行。

中间件页面在水合期间运行于服务端和客户端。插件可以运行在服务端、客户端或两者。组件也可以被强制为仅客户端运行。组合式函数工具函数则根据它们的使用上下文来决定运行位置。

服务端渲染的好处:

  • 性能:用户可以立即访问页面内容,因为浏览器显示静态内容的速度远比显示 JavaScript 生成的内容快。与此同时,Nuxt 在水合过程中保留了 Web 应用的交互能力。
  • 搜索引擎优化(SEO):通用渲染会像传统的服务端应用那样,将页面的完整 HTML 内容交付给浏览器。网络爬虫可以直接索引页面内容,这使得通用渲染成为任何你希望被快速索引的内容的理想选择。

服务端渲染的缺点:

  • 开发约束:服务端和浏览器环境提供的 API 并不相同,编写能无缝运行在两端上的代码可能很棘手。不过,Nuxt 提供了指导原则和特定变量来帮助你判断某段代码在哪里执行。
  • 成本:为了让页面按需渲染,需要有一个运行中的服务器。这就像任何传统服务器一样,会产生月度成本。不过,由于浏览器在客户端导航时接管了后续请求,服务端调用被大幅减少。通过利用边缘端渲染还可以进一步降低成本。

通用渲染非常通用,几乎能适应任何用例,尤其适合任何面向内容的网站:博客、营销网站、作品集、电商网站和集市

关于如何编写不会出现水合不匹配(hydration mismatch)的 Vue 代码,请参阅 Vue 文档

当导入一个依赖浏览器 API 且带有副作用的库时,请确保导入它的组件只在客户端被调用。打包工具不会对包含副作用的模块导入进行 tree-shaking。

客户端渲染(Client-Side Rendering)

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

客户端渲染的好处:

  • 开发速度:当完全在客户端工作时,我们无需担心代码的服务端兼容性,例如使用像 window 对象这样仅限浏览器的 API。
  • 更便宜:运行服务器会增加基础设施成本,因为你需要运行在一个支持 JavaScript 的平台上。我们可以将纯客户端应用托管在任何带有 HTML、CSS 和 JavaScript 文件的静态服务器上。
  • 离线:由于代码完全运行在浏览器中,它可以在网络不可用时很好地继续保持工作。

客户端渲染的缺点:

  • 性能:用户必须等待浏览器下载、解析并运行 JavaScript 文件。这取决于网络(下载部分)和用户设备(解析和执行部分),这可能会耗费一些时间并影响用户体验。
  • 搜索引擎优化:索引和更新通过客户端渲染交付的内容,比使用服务端渲染的 HTML 文档需要更长时间。这与我们讨论过的性能缺点相关,因为搜索引擎爬虫在首次尝试索引页面时,不会等待界面被完全渲染。使用纯客户端渲染,你的内容会在搜索结果页中需要更长时间才能显示和更新。

客户端渲染适合交互性强的Web 应用,这些应用不需要被索引,或者用户会频繁访问。它可以利用浏览器缓存来跳过后续访问时的下载阶段,例如 SaaS、后台应用或在线游戏

你可以在 nuxt.config.ts 中通过 Nuxt 启用仅客户端渲染:

nuxt.config.ts
export default defineNuxtConfig({
  ssr: false,
})
如果你确实使用 ssr: false,你还应该在 ~/spa-loading-template.html 中放置一个 HTML 文件,其中包含你希望用于渲染加载屏幕的 HTML,该屏幕会在你的应用完成水合之前显示。SPA 加载模板·阅读更多