Skip to content

13 · 渲染模式:CSR / SSR / SSG / ISR / RSC ​

本篇解决什么问题: "要不要上 SSR" 是个架构决策,代价是服务端成本 + 复杂度 + 水合开销。 本篇给一张判断表,让你按首屏要求 / 内容更新频率 / 个性化程度直接选,而不是跟风。

📌 关键认知:没有最优模式,只有最匹配业务的模式;而且同一站点不同页面可以用不同模式(Next.js 的核心卖点)。


开篇速览 ​

模式HTML 在哪生成首屏SEO服务端成本适合
CSR浏览器慢(白屏 → 骨架 → 内容)❌ 差无(纯静态托管)后台系统、重交互应用
SSR每次请求时服务端快✅ 好高(每次都算)内容站、电商详情、需 SEO + 实时
SSG构建时最快✅ 好无文档、博客、营销页
ISR构建时 + 按需增量再生成快✅ 好低大量内容页(商品、文章)
RSC服务端组件在服务端渲染,客户端组件在浏览器快✅ 好中React 生态的精细化拆分

一、五种模式逐个拆 ​

1.1 CSR(Client-Side Rendering) ​

浏览器 ──► 空 HTML(只有一个 <div id="root">)
        ──► 下载 JS bundle
        ──► 执行 JS,渲染出 DOM
        ──► 发请求拿数据
        ──► 渲染内容

优点:部署极简(静态文件扔 CDN)、交互体验好、前后端彻底分离。 缺点:首屏白屏、SEO 差、性能依赖用户设备。

💡 Flutter Web 天然只能 CSR:它编译出的就是一个巨大的 JS bundle + canvas 渲染,没有服务端 HTML 生成这一环。

1.2 SSR(Server-Side Rendering) ​

浏览器 ──► 请求
服务端 ──► 执行组件、拉数据、拼出完整 HTML
        ──► 返回 HTML(首屏立即可见)
浏览器 ──► 下载 JS → **水合(Hydration)** → 可交互

水合 = 把服务端渲染的静态 DOM "接上" React/Vue 的事件与状态,让它变成可交互的应用。

代价(必须知道):

代价说明
服务器成本每次请求都要执行组件 + 拉数据
TTFB 变慢服务端要先算完才能返回
水合阻塞HTML 下来了但不能点,要等 JS 下载并执行完
运维复杂度Node 服务要监控、扩容、容灾(见 A23 · Node.js 与 BFF)

1.3 SSG(Static Site Generation) ​

构建时就把所有页面生成成静态 HTML,运行时直接由 CDN 返回。

优点:最快、最安全、成本最低。 缺点:内容变更必须重新构建;页面数多时构建时间线性增长。

📌 本站(sunqy-doc)就是 SSG——VitePress 在构建时生成全部静态页,部署到 Cloudflare Pages。

1.4 ISR(Incremental Static Regeneration) ​

SSG 的增强:静态页面生成后,按过期时间(或按需)在后台重新生成。

js
// Next.js
export async function getStaticProps() {
  return { props: { data }, revalidate: 60 }  // 60 秒内用缓存,过期后后台再生成
}

优点:兼顾 SSG 的速度与内容的相对新鲜;页面再多也不用全量构建。 代价:可能有短暂旧内容(stale-while-revalidate);需要支持 ISR 的平台。

1.5 RSC(React Server Components) ​

不是一个"页面级"模式,而是组件级的拆分:

组件类型在哪执行能做什么不能做什么
Server Component只在服务端直接读数据库/文件系统、代码不进 bundle不能用 useState/useEffect/浏览器 API
Client Component服务端预渲染 + 浏览器水合交互、状态、事件不能直接访问后端资源

核心价值:大幅减少客户端 bundle——服务端组件的依赖(比如一个巨大的 markdown 解析器)不会被下载。


二、选型判断框架(本篇的重点) ​

按三个问题依次回答:

问题 1:需要 SEO / 首屏速度吗? ​

  • ❌ 不需要(后台系统、内部工具、重交互编辑器)→ CSR,别折腾
  • ✅ 需要 → 进入问题 2

问题 2:内容是"相对固定"还是"实时 / 个性化"? ​

  • 相对固定(文档、博客、营销页)→ SSG(最快最省)
  • 实时或个性化(价格、库存、登录后内容)→ SSR
  • 内容多且会更新(商品详情、新闻)→ ISR(SSG 的速度 + 可更新)

问题 3:是否已用 React 且 bundle 体积是瓶颈? ​

  • 是 → 考虑 RSC(把静态展示部分下沉到服务端组件)
  • 否 → 不必为了 RSC 上 RSC

决策速查表 ​

场景推荐
后台管理系统CSR
公司官网 / 营销页SSG
技术文档 / 博客SSG(本站即如此)
电商商品详情(量大、价格会变)ISR
电商首页(强个性化 + SEO)SSR
社交 Feed(登录态、实时)CSR + SSR 首屏 混合
React 应用 bundle 过大RSC 拆分

三、水合(Hydration)的代价:SSR 不是免费的 ​

这是最容易被忽略的代价,也是面试高频追问点。

时间轴:
  ① HTML 返回        → 用户**看到**内容(FCP 好)
  ② JS 下载          → 还不能交互
  ③ JS 执行 + 水合   → 终于能点(TTI)

问题:① 和 ③ 之间有一段 "看得见但点不动" 的恐怖谷。 页面越复杂、bundle 越大,这段越长——用户会疯狂点击但毫无反应。

三种缓解方案:

方案思路
流式 SSR(Streaming)HTML 分块返回,配合 Suspense,先出骨架先水合
选择性水合(Selective Hydration)按交互优先级分批水合,谁先被点谁先水合(React 18+)
岛屿架构(Islands)只有交互"岛屿"需要 JS,其余全是静态 HTML(Astro 的核心)

✅ 理解"恐怖谷"是区分背概念与真懂的分水岭。


四、SSR 落地要额外处理的事 ​

上 SSR 前,这些问题必须有答案:

问题说明
服务端没有 window组件里访问 window/document/localStorage 会崩 → 必须放在 useEffect 或做 typeof window !== 'undefined' 判断
数据请求重复服务端拉了一次,水合时又拉一次 → 需要把服务端数据序列化注入到 HTML(如 Next.js 的 __NEXT_DATA__)
样式闪动(FOUC)服务端要能提取并内联关键 CSS
缓存与扩容SSR 是 CPU 密集,需要缓存 HTML 片段 + 弹性扩容
错误边界服务端渲染抛错会导致整页 500,必须有兜底

自检清单 ​

  • [ ] 能说出 5 种模式各自的 HTML 生成时机与适用场景
  • [ ] 能解释水合是什么,以及它带来的"看得见但点不动"问题
  • [ ] 能按"SEO 需求 → 内容更新频率 → bundle 瓶颈"三步给出选型
  • [ ] 能说出 SSR 落地的 3 个以上额外成本(服务端无 window、数据重复请求、FOUC 等)
  • [ ] 能解释为什么 Flutter Web 只能 CSR

常见误区 ​

❌ 误区✅ 事实
"SSR 一定比 CSR 快"SSR 的 TTFB 更慢、TTI 可能更慢;赢的是 FCP 和 SEO
"SSG 的页面不能更新"重新构建即可;量大时用 ISR
"RSC 是 SSR 的替代品"RSC 是组件级拆分,可与 SSG/ISR 组合
"上了 SSR 就不用管 bundle 体积"恰恰相反:bundle 越大,水合越慢,恐怖谷越长

动手练习 ​

  1. 用 VitePress 新建一个页面,npm run docs:build 后到 dist 里确认它是纯静态 HTML(体会 SSG)。
  2. 找你现在项目的一个页面,按"三问框架"判断它应该用哪种模式,并写下理由。
  3. 在 Chrome 里开 CPU 4× 降速,观察一个 SSR 站点的"看得见但点不动"窗口有多长。

相关文档 ​