前端渲染方案选型指南:从 CSR 到 PPR
摘要总结:针对前端渲染方案选型难题,系统解析 CSR、SSR、SSG、ISR、PPR 五种方案的核心原理、优缺点与适用场景,提出“构建时机选择 × 渲染位置选择”的分析框架。通过对比性能、实时性、SEO、架构复杂度四个维度,构建选型决策树,并以 VitePress 为例展示 SSG 的落地实践。总结出不同业务场景下的最优渲染策略,为前端架构决策提供参考。
1. 引言
一个 Web 网站(对应用也适用)从源代码到最终呈现在用户眼前,大致经历以下四个阶段:
提示
"构建 → 部署 → 请求 → 渲染",不同方案的本质差异 = 构建阶段的时机选择 × 渲染阶段的位置选择。
1、构建:将源代码经过编译、转译等构建步骤,生成可部署的产物。
2、部署:将构建产物部署到服务器,使其可以通过网络访问。
3、请求:用户通过浏览器输入 URL 发起请求,服务器接收并处理该请求。
4、渲染:客户端(浏览器)接收服务器返回的内容,将其渲染为可视化页面。
上述流程中,"构建"和"渲染"两个阶段存在多种技术选择,不同的选择组合便构成了不同的渲染方案,这也是本文要讨论的核心问题——CSR、SSR、SSG 以及 ISR 四种方案各自的原理、适用场景以及如何选型。
2. CSR 渲染方案
CSR(Client-Side Rendering,客户端渲染)将整个渲染过程放在浏览器端完成,是单页应用(SPA)最典型的渲染方式。服务器只返回一个近乎空的 HTML 框架和一份 JavaScript Bundle,页面内容的生成与渲染完全由客户端 JS 驱动。
核心原理
服务端仅返回基础 HTML(无实际内容)和 JS Bundle,客户端完成 DOM 构建与渲染,例如 React 的 createRoot().render() 或 Vue 的 createApp().mount()。
用户访问 URL
↓
服务器返回:空 HTML 骨架(通常只有 <div id="app"></div>)
↓
浏览器下载 JS Bundle 和 CSS
↓
浏览器执行 JS,动态生成 DOM,渲染页面
↓
页面呈现 & 可交互优缺点
| 优点 | 缺点 |
|---|---|
| 首屏之后交互流畅,适合复杂交互场景 前端独立开发,服务端只需提供数据接口(API) | 首屏加载慢——用户必须等待 JS 下载、执行完成后才能看到内容 SEO 不友好——搜索引擎爬虫抓取到的 HTML 没有实际内容 |
适用与不适用场景
本质权衡:CSR 以"初始白屏"为代价,换取首屏之后的流畅交互体验。FCP(首次内容绘制)完全取决于 JS Bundle 的下载与执行速度。对于任何面向用户、首屏体验重要的场景,通常不是最优选择。
| 适用场景 | 不适用场景 |
|---|---|
| · 需要登录的后台管理系统(SEO 无关,首屏要求不高) · 内部工具类应用 · 高交互富应用(如 Figma、Google Docs 等) | · 需要良好 SEO 的内容型网站(博客、官网、电商详情页等) · 首屏体验重要的面向用户的产品 |
3. SSR 渲染方案
SSR(Server-Side Rendering,服务端渲染)在服务器接收到请求时实时执行页面代码、生成完整 HTML 并返回给浏览器,是与 CSR 相对的一种方案。服务器返回的 HTML 已经包含页面内容,浏览器可以直接呈现,随后进行"水合"(Hydration)使页面可交互。React 的 renderToString()、Vue 的 renderToString() 都是典型的服务端渲染 API。
代表框架:Next.js、Nuxt.js、SvelteKit。
核心原理
服务器在接收到请求时,实时执行页面组件代码,将渲染结果(包含数据的完整 HTML)返回给浏览器。浏览器直接显示内容,无需等待 JS 执行;随后加载 JS 并完成"水合",页面才真正具备交互能力。
用户访问 URL
↓
服务器接收请求,执行 Vue/React 组件(如 renderToString())
↓
生成完整带内容的 HTML,立即返回给浏览器
↓
浏览器直接显示页面内容(首屏极快)
↓
JS 加载完成后"水合"(Hydration),页面可交互优缺点
| 优点 | 缺点 |
|---|---|
| 首屏快——服务器返回的 HTML 已包含完整内容,浏览器可直接呈现 SEO 友好——搜索引擎爬虫能抓取到完整内容 | 服务器压力大——每个请求都需要服务器实时渲染,增加计算开销 TTI 可能延迟——页面虽已可见,但需等待 JS 水合完成后才可交互 |
适用与不适用场景
本质权衡:SSR 以服务器实时渲染为代价,换取首屏速度和 SEO 效果。水合过程完成后用户体验与 CSR 无异,但水合本身也是一次额外的性能开销,需注意避免"水合延迟"问题。
| 适用场景 | 不适用场景 |
|---|---|
| · 内容型网站(博客、新闻站、文档站) · 需要良好 SEO 的营销落地页 · 需要首屏快速呈现的面向用户的产品 | · 高度交互的富应用(后台系统、数据可视化平台) · 请求量极大、服务器成本敏感的高流量场景 · 强依赖客户端个性化数据的页面 |
4. SSG 渲染方案
SSG(Static Site Generation,静态站点生成)在构建阶段提前执行页面代码,将渲染出的完整 HTML 预先生成好,部署时直接将这些静态文件分发到 CDN 或静态服务器。用户访问时,服务器无需任何计算,直接返回现成的 HTML 文件。
代表框架:Gatsby、Next.js(getStaticProps / generateStaticParams)、VitePress、Docusaurus。
核心原理
构建阶段(npm run build)调用数据接口,执行页面组件代码,生成所有页面的静态 HTML 文件并输出到 dist 目录。部署后用户发起请求时,CDN 直接返回预渲染文件,全程无服务器动态计算。
开发完成,执行 npm run build / generate
↓
构建阶段生成所有页面的 .html 文件
↓
部署到 CDN 或静态服务器
↓
用户访问 → CDN 直接返回现成的 HTML(无服务器计算)优缺点
| 优点 | 缺点 |
|---|---|
| 性能极高——HTML 来自 CDN 缓存或直接文件系统读取,无需服务器计算 安全性好——纯静态文件,无服务端代码执行面 SEO 友好——搜索引擎抓取到完整内容 | 数据更新需重新构建——内容变更必须重新触发构建 不适用于数据频繁变化的场景——每次更新都要全量重新生成 |
适用与不适用场景
本质权衡:SSG 以"放弃实时渲染能力"为代价,换取极高的加载性能和服务器端的安全性、稳定性。适合内容相对固定、以阅读为主的场景。
| 适用场景 | 不适用场景 |
|---|---|
| · 文档站(GitBook、VitePress、Docusaurus) · 营销落地页、产品官网 · 博客、个人站点等内容相对稳定的网站 | · 数据频繁更新的页面(如股票行情、实时数据看板) · 需要根据用户身份个性化展示的内容 · 页面数量极多且无法穷举的场景(如电商搜索结果页) |
5. ISR 渲染方案
ISR(Incremental Static Regeneration,增量静态再生)是 SSG 与 SSR 的混合方案,由 Next.js 在 9.5 版本引入。其核心思想是:不需要为所有页面在构建时一次性生成静态文件,而是在运行时按需"渐进式"生成静态页面——页面在首次访问时自动生成静态版本,后续请求直接复用缓存,只有超过指定周期或收到特定触发时才重新生成。
这种模式兼具 SSG 的高性能和 SSR 的灵活性,尤其适合页面数量庞大但又无法全部预构建的大型网站。
代表框架:Next.js(9.5+,通过 getStaticProps 的 revalidate 参数,以及 App Router 中的 revalidate 导出或 revalidatePath / revalidateTag 按需失效机制)。
核心原理
ISR 的工作机制基于 HTTP stale-while-revalidate 缓存策略:
- 首次访问(MISS):页面尚未生成静态版本,服务器实时渲染一次,生成静态 HTML 并存入缓存,同时标记下次过期时间。
- 缓存期内(STALE):用户访问直接返回缓存的静态 HTML,速度与 SSG 完全一致。
- 过期后(REVALIDATION):后台静默重新生成新版本,下次访问时用户获得最新页面,缓存无缝切换。
配置通过 Next.js 的 revalidate 字段控制,单位为秒(revalidate: 60 表示 60 秒后过期)。
用户首次访问页面(缓存 MISS)
↓
服务器实时渲染,生成静态 HTML 并写入缓存
↓
后续用户在 revalidate 周期内访问(缓存 HIT/STALE)
↓
直接返回缓存的静态 HTML(极速加载)
↓
超过 revalidate 周期后(后台触发 REVALIDATION)
↓
后台静默重新渲染,更新缓存
↓
下一位用户获得最新页面ISR 的两种模式
| 模式 | 说明 | 适用场景 |
|---|---|---|
| 按路由周期失效 | 整页按统一 revalidate 周期更新 | 资讯列表、博客首页、商品分类页 |
| 按需失效(On-Demand ISR) | 通过 revalidatePath() / revalidateTag() 手动触发 | 电商详情页(库存、价格变化时主动更新) |
按需失效是 ISR 的重要能力——CMS 更新一篇文章时,不必触发全站重新构建,只需声明性清理特定路径或缓存标签的缓存即可。
优缺点
| 优点 | 缺点 |
|---|---|
| 保留 SSG 的性能优势——缓存命中时页面加载极快,无服务器动态计算 支持内容更新——无需全站重新构建,页面可自动或手动刷新 适合大型站点——无需在构建时穷举所有页面,边际成本低 灵活的更新策略——按路由周期失效和按需失效可组合使用 | 数据更新有延迟——取决于 revalidate 周期设置,周期太长则内容不及时架构复杂度较高——需要理解缓存策略与失效机制的配合 不适合极强实时性要求的场景——如股票行情、实时水位等毫秒级数据 |
适用与不适用场景
本质权衡:ISR 以"放弃绝对实时性"为代价,在 SSG 的高性能与 SSR 的灵活性之间取得平衡。适合那些内容需要更新、但更新频率不至于需要每次请求都实时渲染的场景。它将构建时生成的静态页面从"一次性产物"变成了"可持续更新的缓存资产"。
| 适用场景 | 不适用场景 |
|---|---|
| · 电商商品详情页、分类列表(数据更新频繁但非实时) · 资讯文章列表和详情页(CMS 更新后按需刷新) · 页面数量庞大、无法全部构建的大型内容站 · 需要良好 SEO 且首屏性能要求高的页面 | · 对数据实时性要求极高的场景(股票、外汇、实时监控面板) · 完全个性化、千人千面的页面(每次请求内容均不同) · 数据完全公开但更新极频繁且不能有任何延迟的页面 |
6. PPR 渲染方案
PPR(Partial Prerendering,部分预渲染)是一种流式混合渲染策略,其核心思想是:将页面拆分为静态外壳和动态组件,静态部分在服务端预渲染并立即返回,动态部分通过流式加载逐步呈现——用户无需等待所有内容就绪就能看到完整页面结构。
与 ISR 按页面粒度混合不同,PPR 的混合粒度是组件级别,同一个页面中不同组件可以采用不同的渲染策略。
核心原理
PPR 的实现依赖流式服务端渲染(Streaming SSR)能力。当服务端处理一个页面请求时,会分两部分工作:
- 静态外壳(Static Shell):不涉及动态数据的组件(如页面布局骨架、文章正文框架)立即渲染并生成完整 HTML,伴随动态组件的加载占位符一起返回,浏览器立即呈现页面框架。
- 动态组件:被加载占位符包裹的动态部分(如用户头像、评论列表)独立执行渲染,通过 HTTP 流式分块返回,浏览器逐步消费并替换对应的占位内容。
整个过程用户感知到的是页面在"一点点长出来",而非 SSR 中需要等待所有数据就绪才能看到任何内容。
用户访问 URL
↓
服务端立即返回静态 HTML 外壳(页面骨架已呈现)
↓
浏览器显示页面框架(包含动态组件加载占位符)
↓
服务端流式推送动态组件渲染结果
↓
动态部分逐一填充,页面完整呈现与 ISR 的本质区别
| 维度 | ISR | PPR |
|---|---|---|
| 混合粒度 | 页面级别——整页一起缓存/失效 | 组件级别——不同组件独立缓存/流式渲染 |
| 缓存策略 | 按页面整体缓存,存在统一 revalidate 周期 | 静态外壳和动态组件各有独立的缓存语义 |
| 渲染时机 | 首次访问时生成静态版本,后续复用 | 静态外壳每次请求实时渲染,动态组件独立流式 |
| 内容新鲜度 | 依赖 revalidate 周期,有一定延迟 | 静态外壳每次请求新渲染,内容始终最新 |
PPR 可以看作是 ISR 思想的进一步细化——ISR 把整页当作一个整体进行增量更新,PPR 将渲染策略拆分到组件级别,让静态内容与动态内容各取所需。
注:PPR 的概念最早由 Next.js 14 提出并实现,随后被其他框架逐步借鉴。当前生产级实现仍以 Next.js 为主,但其核心机制(静态外壳 + 流式动态组件)是一种与框架无关的渲染思想。
优缺点
| 优点 | 缺点 |
|---|---|
| 首屏极快——静态外壳无任何动态延迟,页面框架立即呈现 动态内容无缝加载——占位符逐步填充,用户体验流畅 组件级混合灵活性——开发者按组件粒度控制渲染策略,无需整页一刀切 TTI 更优——HTTP 流式传输使页面可交互时间早于传统 SSR | 需要框架深度支持——依赖流式渲染和组件级边界划分机制,配置门槛较高 调试复杂度高——静态外壳和动态组件的边界设计需要谨慎规划 属于前沿方案——各框架实现进度不一,API 稳定性仍在演进中 过度工程化风险——对简单页面反而引入不必要的架构复杂度 |
适用与不适用场景
本质权衡:PPR 以组件级拆分 + 流式渲染为手段,试图解决 SSR"等全部内容才呈现"和 SSG"动态内容必须等待构建"的固有矛盾。它代表了一种趋势——渲染策略从页面级别下沉到组件级别。但鉴于其前沿性,建议在非核心页面验证后再应用于生产环境。
| 适用场景 | 不适用场景 |
|---|---|
| · 动态内容与静态内容交织的页面(如文章页:正文静态 + 评论/点赞动态) · 需要快速首屏的仪表盘类页面(静态框架 + 数据卡片流式加载) · 大型内容站中同时包含个性化推荐和公共内容的混合页面 · 需要 SEO 同时又想优化可交互时间的复杂页面 | · 页面结构简单、全部为静态内容的页面(SSG 足够,无需引入额外复杂度) · 高度个性化、每个用户看到内容完全不同的页面(SSR 或 CSR 更合适) · 追求框架 API 稳定性的生产环境项目(PPR 各框架实现进度不一) |
7. 关键差异点分析与实践选择指南
7.1 五种方案综合对比
| 维度 | CSR | SSR | SSG | ISR | PPR |
|---|---|---|---|---|---|
| HTML 生成时机 | 客户端运行时 | 每次请求时实时渲染 | 构建时一次性生成 | 首次访问时生成,后续按周期或按需更新 | 服务端每次请求渲染静态外壳,动态组件独立流式返回 |
| HTML 生成次数 | 每次访问都重新生成 | 每次请求都重新生成 | 一次(构建时) | 首次生成后缓存,过期后重新生成 | 静态外壳每次请求新渲染,动态组件独立缓存/流式 |
| 数据实时性 | 实时(客户端获取) | 实时(请求时获取) | 静态(构建时固化) | 近实时(依赖 revalidate 周期) | 静态外壳实时,动态组件取决于其刷新策略 |
| TTFB | 慢(需等 JS 下载执行后才开始接收内容) | 中等(服务端渲染需要时间) | 极快(CDN 直接返回) | 快(缓存命中时等同 SSG) | 快(静态外壳立即返回,无需等待动态数据) |
| SEO 友好度 | ❌ 不友好(爬虫抓不到内容) | ✅ 友好(完整 HTML) | ✅ 友好(完整 HTML) | ✅ 友好(完整 HTML) | ✅ 友好(静态外壳包含完整页面结构) |
| 服务器压力 | 极低(仅提供静态资源) | 高(每次请求都需渲染) | 极低(纯 CDN) | 低(缓存命中时无服务端计算) | 中等(静态外壳每次需渲染,但可 CDN 缓存) |
| 水合(Hydration) | 无需 | 需要(水合开销大) | 少量或不需要 | 需要(静态页面水合) | 部分需要(静态外壳无需或少量水合,动态组件独立渲染) |
| CDN 缓存能力 | 可缓存 JS Bundle | 难以缓存(动态内容) | 完全支持(静态文件) | 支持(静态页面可缓存,动态部分走 API) | 支持(静态外壳可缓存,动态组件走流式) |
| 构建时间影响 | 无影响 | 无影响 | 随页面数量线性增长(万级页面构建慢) | 初始构建快,后续增量更新 | 无影响(动态组件在请求时渲染) |
| 适用场景 | 强交互、登录后才能访问的内部系统 | 需要 SEO 且数据相对动态的页面 | 内容固定、访问量大的公开页面 | 内容定期更新、页面数量大的内容站 | 动静混合、组件渲染策略各异的复杂页面 |
渲染模式工作分布对比图:
记忆口诀:
- CSR:构建少,服务少,客户端全包 → "客户端累死服务器清闲"。
- SSR:构建少,服务较多 → "服务器打工,客户端躺平"。
- SSG:构建较多,服务少 → "一次构建,多次享用"。
- ISR:构建多,服务多 → "静态优先,动态按需"。
- PPR:构建多,服务多 → "外壳先见,动态后长"。
┌─────────────────────────────────────────────────────────────────────────────┐
│ 渲染工作量分布:Build / Server / Client │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 节点 ──► Build (构建) Server (服务端) Client (客户端) │
│ (编译/转译源代码) (接收请求/处理数据) (浏览器解析/交互) │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ CSR(客户端渲染) │ │
│ │ Build ████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │
│ │ Server ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │
│ │ Client ██████████████████████████████████████████████████████ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ SSR(服务端渲染) │ │
│ │ Build ████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │
│ │ Server ██████████████████████████████████████████████████████ │ │
│ │ Client ████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ SSG(静态站点生成) │ │
│ │ Build ██████████████████████████████████████████████████████ │ │
│ │ Server ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │
│ │ Client ████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ ISR(增量静态再生)⭐ SSG + SSR 的混合 │ │
│ │ Build ████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │
│ │ Server ████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │
│ │ Client ████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ PPR(部分预渲染)⭐ 组件级混合渲染 │ │
│ │ Build ████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │
│ │ Server ████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │
│ │ Client ████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ 图例:■ = 该阶段工作量占比(■ 越多 = 工作量越大) │
│ Build = 源代码编译/转译 Server = 请求处理/渲染 Client = 浏览器执行 │
└─────────────────────────────────────────────────────────────────────────────┘7.3 选型决策树
业务场景诊断
│
├── 是否需要公开搜索引擎收录?(SEO 是否重要)
│ │
│ ├── ✅ 是 → 是否要求毫秒级实时数据?
│ │ │
│ │ ├── ✅ 是 → ✅ SSR(必要时在 SSR 基础上引入 ISR)
│ │ │
│ │ └── ❌ 否 → 页面数量与更新频率如何?
│ │ │
│ │ ├── 页面少(百级以下)、内容固定不变
│ │ │ → ✅ SSG(极致性能,CDN 零计算)
│ │ │
│ │ ├── 页面多(千级以上)或内容频繁更新
│ │ │ → ✅ ISR(兼顾性能与更新能力)
│ │ │
│ │ └── 动静混合(部分静态 + 部分个性化)
│ │ → ✅ PPR(组件级混合渲染)
│ │
│ └── ❌ 否 → 是否需要登录后才可访问?(非公开页面)
│ │
│ ├── ✅ 是 → 强交互 / 富应用?
│ │ ├── ✅ 是(后台系统、数据可视化等)
│ │ │ → ✅ CSR(SEO 不重要,首屏后交互流畅)
│ │ └── ❌ 否 → ✅ SSR + 客户端水合
│ │
│ └── ❌ 否(公开但无 SEO 需求,如内部文档工具)
│ → ✅ SSG 或 CSR(视交互复杂度而定)7.4 常见落地陷阱与解决方案
① SSR 水合(Hydration)导致的页面闪烁与重复渲染
服务端渲染出的 HTML 与客户端首次渲染的 DOM 不一致时,React/Vue 会丢弃服务端 DOM 并重新渲染,导致页面闪烁(Flicker)和性能损耗。常见原因:服务端与客户端使用了不同的日期/随机值/语言环境;直接在组件顶层读取了 window / document 等浏览器对象。
解决方案:使用 useEffect(React)或 onMounted(Vue)包裹浏览器 API 调用,或启用懒水合(Lazy Hydration)策略。
② SSG 构建时间随页面数量爆炸性增长
站点达到数万级页面时,npm run build 可能耗时数小时,严重影响 CI/CD 发布节奏(电商类站点尤为突出)。
解决方案:采用 ISR 按需生成,或使用"预渲染白名单"策略(只预渲染高频访问页面,其余走 ISR/SSR)。
③ ISR 的 revalidate 周期设置两极化
周期过长 → 内容严重滞后(电商价格、库存错误);过短(如 revalidate: 1)→ 实际退化为 SSR,缓存形同虚设。
解决思路:为不同数据新鲜度要求的模块设置不同的刷新周期,通过页面级 ISR + 组件级 API 请求组合实现精细化控制,而非一刀切。
④ CSR/SSR 混用时的 SEO 策略不一致
同一 URL 在不同时间返回的 HTML 内容不一致(时而完整、时而空白),可能被搜索引擎判定为"内容作弊"。
解决思路:在项目初期确定统一的渲染策略,避免同一页面混合使用多种渲染模式。
⑤ SSR 的 API 请求"串行陷阱"
SSR 时组件的 API 请求按组件树串行发起(Component A → Component B → Component C),导致 TTFB 线性累加:100ms + 200ms + 150ms = 450ms。
解决思路:在服务端使用 Promise.all 并发发起所有数据请求,或利用 ISR 将渲染结果缓存。
⑥ PPR 流式渲染的组件边界划分失衡
边界划分过多导致架构碎片化,划分过少则退化为普通 SSR。
实践建议:优先将布局组件、导航、文章正文等固定内容划入静态外壳;将评论、点赞数、推荐内容等个性化组件划入流式动态组件。原则:"固定优先,动态例外"。
8. VitePress:一个典型的 SSG 实践案例
介绍完五种渲染方案,通过一个真实框架来验证理解,是巩固知识的最佳方式。VitePress 正是这样一个典型范例——它既不过于复杂,又足够完整地呈现了 SSG 的核心逻辑。
VitePress 是 Vue 官方推出的下一代文档框架,前身是 VuePress。它的设计目标非常明确:为文档站点提供极致的加载性能和极简的部署模型。Vue 官方文档本身正是跑在 VitePress 上,每天服务数百万次请求,从未需要一台应用服务器——这本身就是对 SSG 能力最有力的背书。
本章将剖析 VitePress 的渲染架构,从构建链路到运行时模型,从框架设计哲学到它给我们的选型启示。
8.1 VitePress 如何落地 SSG
VitePress 的 SSG 实践可以概括为三个关键词:构建时穷举、零运行时、纯静态分发。
① 构建时穷举
VitePress 的文档内容以 Markdown 文件形式存在于 docs/ 目录下,每个 .md 文件对应一个路由页面。
执行 vitepress build 时,Vite 的插件链会遍历所有 Markdown 文件,逐一完成编译:Markdown 经过 markdown-it 解析为 HTML,嵌入的 Vue 组件被 Vue 3 runtime 渲染,最终产出独立的 .html 文件写入 dist/ 目录。
这个过程无需任何服务端参与,纯粹是本地构建工具的批量处理。
② 零运行时
生成的 dist/ 目录是一个完全自包含的静态站点——上传到任意静态文件服务器(或 CDN)即可提供服务。
服务器在运行时不需要执行任何应用代码,不需要连接数据库,不需要调用 API。用户访问任何一个页面,服务器只是从文件系统读取对应的 .html 文件并返回。这正是 SSG 的本质特征:构建时渲染完成,运行时零计算。
③ 纯静态分发
得益于静态产物的极低访问成本,VitePress 支持直接部署到 CDN。用户的请求就近接入 CDN边缘节点,命中缓存时直接从边缘返回 HTML,源站服务器完全不参与响应。这种架构天然具备了极高的并发承载能力和极低的 TTFB,是任何需要服务端实时渲染的方案无法企及的性能基线。
8.2 核心原理
① 构建阶段(Build Time)
VitePress 使用 markdown-it 解析 Markdown,通过 vite-plugin-md 将 Markdown 文件作为 Vue 组件导入。每个 Markdown 文件本质上是一个 .vue 组件,构建时由 Vite 的插件链完成编译——Markdown 内容被编译为 HTML,Vue SFC 的 <script> 和 <style> 被编译为 JS/CSS,最后所有产物写入 dist 目录。
② 运行时(Runtime)
用户请求一个 VitePress 生成的页面时,服务器(任意静态服务器,或 VitePress 的 preview 命令)直接返回预编译好的 .html 文件。浏览器接收到的 HTML 已包含完整的页面内容——这是 SSG 的典型特征:零服务端计算,零动态渲染。
同时,VitePress 在构建阶段会生成一个轻量的客户端 JS Chunk,包含 Vue runtime 和路由逻辑。页面加载后,Vue 完成"水合"(Hydration),页面进入可交互状态。这个 JS Chunk 极小(相比 Next.js 或 Nuxt 的 SSR bundle 轻量得多),因为它不需要包含数据获取逻辑或服务端渲染逻辑。
③ 页面类型
VitePress 将页面分为两类,分别对应不同的文件路径:
| 页面类型 | 文件位置 | 路由 | 渲染方式 |
|---|---|---|---|
| 文档页面 | docs/*.md | /guide/getting-started | SSG(构建时生成) |
| 布局页面 | docs/index.md | / | SSG(构建时生成) |
所有页面都在构建时生成,运行时无任何服务端组件参与。
8.3 从 VitePress 学到的五件事
① 构建工具的选择直接决定开发体验上限
VitePress 的快,根源不在于"用了 Vite",而在于它正确地选择了只在构建阶段处理所有复杂计算。如果当初迁移时选择了保留 SSR 运行时,那么即使用 Vite 替换了 Webpack,数据获取和渲染的额外耗时依然存在。SSG 的性能优势,一半靠静态产物的缓存,一半靠构建工具链的高效执行。
② 框架的本质是 DSL 的编译器
VitePress 的 Markdown 拓展机制(frontmatter、代码高亮、内容嵌入)本质上是在 Markdown 语言之上封装了一层 DSL。框架作者不需要从零实现一个编程语言,只需要正确地"翻译"两种语法树——这是现代前端框架最强大的设计哲学之一:不要发明语言,要做好语言之间的桥梁。
③ SSG 的极致简单是精心设计的结果
很多人以为 SSG"不需要架构",其实恰恰相反。VitePress 之所以能保持简洁,是因为它的设计者在架构层面做了大量克制:
- 不做服务端路由、不做 API 层、不做运行时数据获取、不做用户认证……
- 每少一个功能,就少一分运行时复杂度。但这些"少"是精心设计的结果,不是偷懒。
真正好的框架,是在满足需求的前提下,保持最小的运行时刻面。
④ 静态站点不等于"没有交互"
VitePress 构建出的页面水合后具备完整的 Vue 响应式能力,可以承载搜索框、主题切换、目录高亮等客户端交互。这些交互完全由客户端 JS Chunk 驱动,与构建阶段的渲染过程解耦。这意味着:SSG 不等于"只能做静态展示",它只是把"渲染"这件事放到了构建阶段,交互层仍然可以是完整的客户端应用。
⑤ 文档站是最纯粹的 SSG 场景
VitePress 非常适合文档站,因为文档站天然具备 SSG 的所有优点:内容固定、页面数量可穷举、不需要实时数据、SEO 重要、访问量波动大(热门文档可能被大量并发访问,需要 CDN 缓存)。 反过来理解:如果文档站不用 SSG,而用 SSR,除了增加服务器成本,没有任何实际收益——这是判断"是否应该用 SSG"最简洁的思路:如果你的页面内容在构建时就已知且不变,SSG 就是最优解,不需要为它增加服务端运行时。
小结:
VitePress 是一个"恰到好处"的框架——它没有试图做 SSR,也没有试图做 ISR/PPR,它只是在 SSG 这条路上走到了极致。理解 VitePress 的渲染链路,能帮助我们更深刻地理解 SSG 的本质:构建时穷举所有可能,用极致的静态换极致的性能。 当你下次在选型时犹豫不决,不妨回想 VitePress 的设计哲学:最简单的架构,往往是正确答案。
9. 结语
在实际工程实践中,并不存在放之四海而皆准的"最优"渲染方案,只有与业务场景高度契合的适配策略。这一判断的底层逻辑,正是本文开篇所提出的核心公式:渲染方案 = 构建时机选择 × 渲染位置选择。
CSR、SSR、SSG、ISR、PPR 五种方案,本质上都是在性能、实时性、SEO、架构复杂度这四个维度之间做权衡取舍。脱离具体业务场景谈技术选型毫无意义——后台管理系统强行套用 SSG 是自缚手脚,文档站点盲目使用 SSR 则是画蛇添足。
值得铭记的几条原则:
- SSG 是性能的极限,代价是放弃实时性;SSR 是灵活性的极限,代价是服务器开销。ISR 和 PPR 则是在两者之间寻找平衡的艺术。
- 渲染方案不是一成不变的。同一个产品的不同页面,完全可以采用不同的渲染策略——首页 SSG、商品详情页 ISR、文章正文 SSG + 评论 CSR,组合使用才是真实战。
- 避免过度工程化。PPR 很酷,但简单页面用 SSR 就足够了。技术选型的成熟标志,是知道什么时候不该用它。
愿本文能帮助你在下一个项目中做出更清晰、更笃定的技术决策。