---
title: "渲染模式"
description: "了解 Nuxt 中可用的不同渲染模式。"
canonical_url: "https://nuxt.zhcndoc.com/docs/3.x/guide/concepts/rendering"
---
# 渲染模式

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

Nuxt 支持不同的渲染模式，包括[通用渲染](https://nuxt.zhcndoc.com/docs/3.x/guide/concepts/rendering#universal-rendering)、[客户端渲染](https://nuxt.zhcndoc.com/docs/3.x/guide/concepts/rendering#client-side-rendering)，还提供了[混合渲染](https://nuxt.zhcndoc.com/docs/3.x/guide/concepts/rendering#hybrid-rendering)和在 [CDN 边缘服务器](https://nuxt.zhcndoc.com/docs/3.x/guide/concepts/rendering#edge-side-rendering)上渲染应用的可能性。

浏览器和服务器都可以解释 JavaScript 代码，将 Vue.js 组件转化为 HTML 元素。这个步骤称为 **渲染**。Nuxt 支持 **通用** 和 **客户端** 渲染，这两种方法各有优缺点，我们将在下文进行介绍。

默认情况下，Nuxt 使用**通用渲染**来提供更好的用户体验和性能，并优化搜索引擎索引，但你可以通过[一行配置](https://nuxt.zhcndoc.com/docs/3.x/api/nuxt-config#ssr)切换渲染模式。

## 通用渲染

这一步类似于传统的由 PHP 或 Ruby 应用执行的 **服务器端渲染**。当浏览器请求开启了通用渲染的 URL 时，Nuxt 会在服务器环境中运行 JavaScript（Vue.js）代码，并返回一个完整渲染好的 HTML 页面给浏览器。Nuxt 也可能从缓存中返回预先生成的完整 HTML 页。用户会立即获得应用初始内容的全部，相较于客户端渲染更快。

HTML 文档下载完毕后，浏览器会解析它，然后 Vue.js 会接管该文档。之前服务器端运行的相同 JavaScript 代码现在**重新**在客户端（浏览器）后台执行，使页面启用交互性（这就是 **通用渲染**）并将监听器绑定到 HTML 元素。这一步称为 **激活（Hydration）**。激活完成后，页面即可享有动态界面和页面过渡等优势。

通用渲染允许 Nuxt 应用实现快速页面加载，同时保留客户端渲染的好处。此外，由于内容已经存在于 HTML 文档中，爬虫可以无需额外开销直接索引内容。

![用户在 HTML 文档加载时即可访问静态内容。激活则赋予页面交互能力](https://nuxt.zhcndoc.com/assets/docs/concepts/rendering/ssr.svg)

**哪些部分是服务器渲染，哪些是客户端渲染？**

在通用渲染模式下，常有人会问 Vue 文件的哪些部分在服务器和/或客户端执行。

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

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

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

在初次请求时，`counter` 的 ref 因为在 `<p>` 标签内渲染而在服务器中初始化，`handleClick` 中的代码则不会被执行。浏览器激活阶段时，`counter` ref 会重新初始化，`handleClick` 函数最终绑定到按钮上。因此可以合理推断，`handleClick` 的主体始终在浏览器环境运行。

[中间件](https://nuxt.zhcndoc.com/docs/3.x/directory-structure/middleware)和[页面](https://nuxt.zhcndoc.com/docs/3.x/directory-structure/pages)会在服务器运行，并在客户端激活期间运行。[插件](https://nuxt.zhcndoc.com/docs/3.x/directory-structure/plugins)可以在服务器端、客户端或两端渲染。[组件](https://nuxt.zhcndoc.com/docs/3.x/directory-structure/components)也可以被强制仅在客户端运行。[组合式函数](https://nuxt.zhcndoc.com/docs/3.x/directory-structure/composables)和[工具函数](https://nuxt.zhcndoc.com/docs/3.x/directory-structure/utils)则根据其使用上下文进行渲染。

**服务器端渲染的优点：**

- **性能**：用户可以立即访问页面内容，因为浏览器可以比 JavaScript 生成内容更快地显示静态内容。同时，Nuxt 在激活过程中保持了 web 应用的交互性。
- **搜索引擎优化（SEO）**：通用渲染将页面完整 HTML 内容像经典服务器应用一样交付给浏览器。网络爬虫能直接索引页面内容，这使通用渲染成为希望快速被索引内容的理想选择。

**服务器端渲染的缺点：**

- \*\*开发限制：\*\*服务器和浏览器环境提供的 API 并不相同，因此编写能在两端无缝运行的代码可能颇具挑战。幸运的是，Nuxt 提供了指南和特定变量，帮助你确定代码在哪一端执行。
- \*\*成本：\*\*需要运行服务器才能即时渲染页面。这会像任何传统服务器一样产生每月费用。不过，由于客户端导航时浏览器会接管，通用渲染大幅减少了服务器调用次数。利用[边缘渲染](https://nuxt.zhcndoc.com/docs/3.x/guide/concepts/rendering#edge-side-rendering)可以降低成本。

通用渲染非常灵活，几乎适用于所有应用场景，特别适合内容导向型网站：**博客、营销网站、作品集、电商及市场平台。**

::tip
想了解更多关于编写避免激活不匹配的 Vue 代码示例，请参阅 [Vue 文档](https://vue.zhcndoc.com/guide/scaling-up/ssr.html#hydration-mismatch)。
::

::important
当导入依赖浏览器 API 且具有副作用的库时，确保导入该库的组件仅在客户端调用。打包器不会对带有副作用的模块导入进行摇树优化。
::

## 客户端渲染

传统的 Vue.js 应用默认在浏览器（即**客户端**）中渲染。浏览器下载并解析包含当前界面构造指令的所有 JavaScript 代码后，Vue.js 才开始生成 HTML 元素。

![用户必须等待浏览器下载、解析和执行 JavaScript 后才能看到页面内容](https://nuxt.zhcndoc.com/assets/docs/concepts/rendering/csr.svg)

**客户端渲染的优点：**

- **开发速度**：完全客户端开发时，无需担心代码的服务器兼容性问题，例如可以直接使用只运行在浏览器的 API，如 `window` 对象。
- **成本较低**：不需要运行服务器增加基础设施成本。客户端应用可以托管在任何静态服务器，只需提供 HTML、CSS 和 JavaScript 文件。
- **脱机可用**：代码完全运行在浏览器，断网时仍然可以正常工作。

**客户端渲染的缺点：**

- **性能**：用户必须等待浏览器下载、解析和执行 JavaScript 文件。下载依赖网络状况，解析和执行受用户设备影响，这可能增加等待时间，影响用户体验。
- **搜索引擎优化**：客户端渲染内容的索引和更新速度比服务器渲染 HTML 慢。搜索引擎爬虫通常不会等待页面完全渲染后再进行索引，因此页面内容显示和更新会延迟。

客户端渲染适合交互性强且不需索引或用户频繁访问的**Web 应用**。可利用浏览器缓存跳过后续访问的下载阶段，例如 **SaaS、后台管理系统或在线游戏**。

可在 `nuxt.config.ts` 中通过设置启用仅客户端渲染：

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  ssr: false,
})
```

::note
如果使用 `ssr: false`，还应在 `~/app/spa-loading-template.html` 放置一个 HTML 文件，其中包含你想用于渲染加载屏幕的 HTML。在应用激活之前，都会显示该加载屏幕。
:read-more{title="SPA 加载模板" to="https://nuxt.zhcndoc.com/docs/3.x/api/configuration/nuxt-config#spaloadingtemplate"}
::

::video-accordion{title="观看 Alexander Lichter 关于使用 Nuxt 构建纯 SPA 的视频" videoId="7Lr0QTP1Ro8"}
::

### 部署静态客户端渲染应用

如果使用 `nuxt generate` 或 `nuxt build --prerender` 命令将应用部署到[静态托管](https://nuxt.zhcndoc.com/docs/3.x/getting-started/deployment#static-hosting)，Nuxt 默认会将每个页面渲染为单独的静态 HTML 文件。

::warning
若用 `nuxt generate` 或 `nuxt build --prerender` 命令预渲染应用，则无法使用任何服务器端点，因为输出目录不会包含服务器代码。如需服务器功能，请使用 `nuxt build`。
::

纯客户端渲染时，通常只需一个 `index.html` 文件，再加上 `200.html` 和 `404.html` 作为回退文件，静态网站托管服务可针对所有请求返回它们。

为此，我们可以更改路由的预渲染方式。只需将以下内容添加到 `nuxt.config.ts` 中的[钩子](https://nuxt.zhcndoc.com/docs/3.x/api/advanced/hooks#nuxt-hooks-build-time)：

```ts twoslash [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](https://nuxt.zhcndoc.com/docs/3.x/getting-started/prerendering#prerendergenerate-nitro-hook) 的 `'prerender:generate'` 钩子。

```ts twoslash [nuxt.config.ts]
// @errors: 2353 7006
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 服务器会自动注册对应的中间件，并利用 [Nitro 缓存层](https://nitro.zhcndoc.com/guide/cache)包裹路由的缓存处理。

```ts twoslash [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 路由加上跨域请求头
    '/api/**': { cors: true },
    // 旧页面重定向
    '/old-page': { redirect: '/new-page' }
  }
})
```

### 路由规则

可用属性如下：

- `redirect: string`{lang="ts"} - 定义服务器端重定向。
- `ssr: boolean`{lang="ts"} - 禁用部分应用区域的服务器端渲染，仅在浏览器渲染（`ssr: false`）。
- `cors: boolean`{lang="ts"} - 自动添加 CORS 请求头（`cors: true`），可用 `headers` 自定义。
- `headers: object`{lang="ts"} - 为应用区域添加自定义请求头，例如静态资源。
- `swr: number | boolean`{lang="ts"} - 添加缓存响应头，在服务器或反向代理端缓存一段 TTL（存活时间）。Nitro 的 `node-server` 预设支持完整响应缓存。TTL 过期后，将返回缓存内容，同时后台重新生成。当为 `true` 时，添加 `stale-while-revalidate` 头且无 MaxAge。
- `isr: number | boolean`{lang="ts"} - 与 `swr` 功能类似，同时支持 CDN 缓存（目前仅 Netlify 和 Vercel）。为 `true` 时，内容在 CDN 中缓存直到下一次部署。
- `prerender: boolean`{lang="ts"} - 在构建时预渲染路由，包含为静态资源。
- `noScripts: boolean`{lang="ts"} - 禁止渲染 Nuxt 脚本和 JS 资源提示。
- `appMiddleware: string | string[] | Record<string, boolean>`{lang="ts"} - 指定哪些中间件应在 Vue 应用部分运行（不包括 Nitro 路由）。

::note
使用 `isr` 或 `swr` 的路由也会在 HTML 旁边生成 `_payload.json` 文件。客户端导航会加载这些缓存的 payload，而不是重新获取数据。使用 glob 模式为动态路由配置，例如 `pages/[...slug].vue`：`'/**': { isr: true }`。
::

尽可能情况下，路由规则会自动应用到部署平台的原生规则，以获得最佳性能（目前支持 Netlify 和 Vercel）。

::important
请注意，使用 [`nuxt generate`](https://nuxt.zhcndoc.com/docs/3.x/api/commands/generate) 时无法使用混合渲染。
::

**示例：**

::card-group
  :::card
  ```yaml [props]
  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
  ```
  Nuxt 应用混合渲染部署于 Vercel 示例。
  :::
::

## 边缘渲染

边缘渲染（Edge-Side Rendering，ESR）是 Nuxt 中引入的强大特性，使你的 Nuxt 应用可通过内容分发网络（CDN）边缘服务器更靠近用户进行渲染。借助 ESR，可以提升性能并减少延迟，提供更优体验。

ESR 将渲染过程推送到网络“边缘”——CDN 的边缘服务器。请注意，ESR 更像是一个部署目标，而非真正的渲染模式。

当对某页面发出请求时，最近的边缘服务器会拦截请求，生成该页面的 HTML 并返回给用户。此过程缩短了数据实际传输距离，**降低延迟并更快加载页面**。

借助 [Nitro](https://nitro.build/)（为 Nuxt 提供支持的[服务器引擎](https://nuxt.zhcndoc.com/docs/3.x/guide/concepts/server-engine)），即可实现边缘渲染。它支持跨平台运行于 Node.js、Deno、Cloudflare Workers 等环境。

当前支持可运用 ESR 的平台包括：

- [Cloudflare Pages](https://pages.cloudflare.com) —— 使用 git 集成及 `nuxt build` 命令零配置支持。
- [Vercel Edge Functions](https://vercel.com/features/edge-functions) —— 通过 `nuxt build` 命令及环境变量 `NITRO_PRESET=vercel-edge` 启用。
- [Netlify Edge Functions](https://www.netlify.com/products/#netlify-edge-functions) —— 通过 `nuxt build` 命令及环境变量 `NITRO_PRESET=netlify-edge` 启用。

请注意，使用边缘渲染时，可结合路由规则启用混合渲染。

以下开源示例项目部署在上述部分平台：

::card-group
  :::card
  ```yaml [props]
  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 的待办事项应用。
  :::

  :::card
  ```yaml [props]
  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 的可编辑网站，使用通用渲染。
  :::
::


## Sitemap

See the full [sitemap](https://nuxt.zhcndoc.com/sitemap.md) for all pages.
