---
id: frontend.ssr.rendering-modes
title: 渲染模式:CSR / SSR / SSG / ISR、水合、流式渲染和 Nuxt 基础
type: lesson
domain: frontend
stack: [ssr]
tags: [CSR, SSR, SSG, ISR, 水合, Hydration, 流式渲染, Nuxt, SEO]
level: intermediate
prerequisites: [frontend.vue.reactivity-composition]
est_hours: 2.5
related: [frontend.ssr.nextjs-app-router, frontend.fe-testing-perf.index, frontend.ssr.interview]
week: [7]
order: 1
---
# 渲染模式:CSR / SSR / SSG / ISR、水合、流式渲染和 Nuxt 基础
## 解释说明
| 模式 | 在哪里生成 HTML | 优点 | 缺点 | 适合 |
| --- | --- | --- | --- | --- |
| CSR(客户端渲染) | 浏览器执行 JS 后生成 | 服务器简单,交互流畅 | 首屏慢、SEO 差 | 后台系统、登录后的应用 |
| SSR(服务端渲染) | 每次请求时服务器生成 | 首屏快、SEO 好、数据实时 | 服务器压力、需要 Node 服务 | 内容和数据经常变化的公开页面 |
| SSG(静态生成) | 构建时生成 | 最快、可放 CDN | 数据更新要重新构建 | 文档、官网、博客 |
| ISR(增量静态再生成) | 构建时生成 + 过期后后台重新生成 | 兼顾速度和更新 | 有短暂的旧数据 | 商品页、文章页 |
**水合(Hydration)**:服务端返回 HTML 后,浏览器加载 JS,把事件监听和状态“接”到已有的 DOM 上,页面才可以交互。问题:
- 水合不匹配:服务端和客户端渲染结果不一致(用了 `Date.now()`、`Math.random()`、`window`、本地时区),会报警告甚至重新渲染。
- 首屏能看到但点不动(JS 还没加载完):通过减少客户端 JS、部分水合 / 服务端组件、懒加载缓解。
**流式渲染**:服务器先发送页面框架,慢的数据块准备好后再流式补上(React 的 Suspense、Next.js 的 `loading.tsx`),首字节更快。
**Nuxt**(Vue 的全栈框架,当前大版本 Nuxt 4,以官方文档为准:https://nuxt.com/docs):基于文件的路由、`useFetch` / `useAsyncData` 在服务端取数并把结果传给客户端避免重复请求、`server/` 目录写接口(Nitro 引擎)、按路由配置渲染模式(`routeRules`:SSR、预渲染、ISR 类缓存)。
## 案例
电商的帮助中心:常见问题页需要 SEO 和最快的首屏,用 SSG / ISR;智能客服对话需要登录和实时交互,是客户端组件;订单页登录后才能看,用 CSR 或 SSR 都可以,但要注意不能把用户数据缓存到 CDN。
## 代码示例
避免水合不匹配:只在客户端执行的逻辑放到挂载之后。
```vue
{{ now ?? "加载中" }}
```
Nuxt 中服务端取数(结果会序列化到页面中,客户端水合时不会再请求一次):
```vue
```
Next.js 的写法见下一课和 [examples/next-ai-chat](examples/next-ai-chat/)。
## 易错点
- 在服务端渲染阶段访问 `window`、`localStorage`。
- 服务端渲染时使用了全局单例状态(Pinia、模块级变量),导致不同用户的数据串在一起:每个请求要创建新的实例。
- 把带用户信息的 SSR 页面缓存到 CDN。
- SSR 页面里调用很慢的接口,拖慢整个首屏:并行取数、流式渲染、超时降级。
- 以为用了 SSR 就一定更快:服务器离用户远、取数慢时,TTFB 反而变差。
## 面试常问
- CSR、SSR、SSG、ISR 的区别和适用场景?
- 什么是水合?水合不匹配的原因?
- SSR 项目有哪些常见的坑?