前端除了 SSR 和 CSR 以外,常见还有这些渲染方式:
SSG:静态站点生成ISR:增量静态再生成预渲染 PrerenderMPA:多页应用渲染Streaming SSR:流式服务端渲染Islands Architecture:岛屿架构 / 局部水合Partial Hydration:局部水合Resumability:可恢复式渲染Server Components:服务端组件Edge Rendering:边缘渲染
下面按「是什么、适合什么、怎么用」展开讲。
先放一个总览 CSR 和 SSR 不是所有方案的两端,而是两个基础形态。
CSR:浏览器拿到空壳 HTML,再下载 JS,由 JS 渲染页面。SSR:服务器每次请求实时生成 HTML,浏览器拿到可见页面,再水合交互。SSG:构建时提前生成 HTML。ISR:先静态生成,过期后后台重新生成。Prerender:针对 SPA 预先跑一遍页面,产出静态 HTML。Streaming SSR:HTML 不等全部算完,边生成边发送。Islands:页面大部分是静态 HTML,只有局部组件变成交互岛。Server Components:组件在服务端执行,只把结果发给客户端。Edge Rendering:在 CDN 边缘节点完成动态渲染或个性化拼装。
它们实际项目里经常混用,不是互斥关系。
SSG:Static Site Generation SSG 是构建时渲染。页面 HTML 在 npm run build 时就生成好了,请求来了直接返回静态文件。
典型流程:
text
数据源 / Markdown / CMS / 接口
↓
构建阶段执行页面逻辑
↓
生成 HTML / CSS / JS
↓
部署到 CDN
↓
用户访问时直接命中静态 HTML
适合:
- 官网
- 文档站
- 博客
- 活动页
- 商品详情中变化不频繁的部分
- SEO 要求高,但实时性不强的页面
优点:
- 首屏快
- CDN 友好
- 服务器压力小
- SEO 好
- 稳定性高
缺点:
- 数据更新需要重新构建
- 页面数量很多时构建慢
- 不适合强实时、强个性化页面
在 Next.js 里可以这样用:
tsx
export async function getStaticProps() {
const product = await fetchProduct();
return {
props: {
product
}
};
}
export default function ProductPage({product}) {
return <div>{product.name}</div>;
}
如果是 Next.js App Router:
tsx
export const dynamic = 'force-static';
export default async function Page() {
const product = await getProduct();
return <div>{product.name}</div>;
}
在 Nuxt 里类似:
bash
nuxi generate
然后把生成出来的静态产物部署到 CDN。
ISR:Incremental Static Regeneration ISR 可以理解成「可自动刷新的 SSG」。
它不是每次请求都 SSR,也不是永远静态。它会先返回已有的静态 HTML,超过缓存时间后,后台重新生成新 HTML。
典型流程:
text
第一次请求
→ 没有缓存
→ 生成 HTML
→ 存起来
后续请求
→ 直接返回缓存 HTML
超过 revalidate 时间
→ 先返回旧 HTML
→ 后台重新生成
→ 下次请求拿到新 HTML
适合:
- 商品详情页
- 资讯详情页
- 店铺页
- 列表聚合页
- 页面很多,但内容不是秒级变化的业务
优点:
- 接近静态站的性能
- 又能定期更新数据
- 比 SSR 压力小
- 比纯 SSG 更灵活
缺点:
- 用户可能短时间看到旧数据
- 对部署平台有要求
- 缓存失效策略要设计清楚
Next.js Pages Router 示例:
tsx
export async function getStaticProps() {
const product = await fetchProduct();
return {
props: {
product
},
revalidate: 60
};
}
这表示页面最多缓存 60 秒,过期后会触发重新生成。
App Router 示例:
tsx
export const revalidate = 60;
export default async function Page() {
const product = await fetchProduct();
return <div>{product.name}</div>;
}
也可以对单个请求设置:
tsx
const res = await fetch('https://api.example.com/product', {
next: {
revalidate: 60
}
});
Prerender:预渲染 Prerender 常用于已有 SPA 项目。它会在构建阶段用无头浏览器或构建插件访问指定路由,把最终 HTML 保存下来。
它和 SSG 的区别是:
SSG通常是框架原生支持,数据和路由在构建阶段有明确模型。Prerender更像是「跑一遍页面,把结果拍下来」。
适合:
- 已经是 Vue SPA / React SPA,不想大改 SSR
- 只想让少数页面 SEO 更好
- 首页、活动页、落地页需要静态 HTML
Vue SPA 常见方式:
js
// vue.config.js
const PrerenderSPAPlugin = require('prerender-spa-plugin');
module.exports = {
configureWebpack: {
plugins: [
new PrerenderSPAPlugin({
staticDir: path.join(__dirname, 'dist'),
routes: ['/', '/about', '/product']
})
]
}
};
产物会包含这些路由对应的 HTML。
优点:
- 改造成本比 SSR 小
- 能改善 SEO 和首屏
- 对少量页面很实用
缺点:
- 动态路由多时不好维护
- 个性化内容不适合
- 构建阶段可能依赖接口稳定性
- 水合后仍然会加载 SPA JS
MPA:Multi Page Application MPA 是传统多页应用。每个页面都是独立 HTML,请求新页面时浏览器整页跳转。
这其实也是一种渲染方式,只是现在大家讨论 SPA 多了,容易忽略它。
适合:
- 页面之间关联弱
- 后台系统的独立模块
- 内容站
- 老项目
- 对首屏和 SEO 有要求,但不想引入复杂 SSR 的场景
典型结构:
text
/index.html
/search.html
/detail.html
/user.html
或者后端模板:
html
<html>
<body>
<h1>{{ product.name }}</h1>
</body>
</html>
优点:
- 简单直观
- SEO 友好
- 首屏稳定
- 单页 JS 体积小
缺点:
- 页面切换体验不如 SPA
- 公共状态管理麻烦
- 多页面构建配置复杂一些
在 Vite 里可以这样配置多入口:
ts
import {defineConfig} from 'vite';
export default defineConfig({
build: {
rollupOptions: {
input: {
index: 'index.html',
search: 'search.html',
detail: 'detail.html'
}
}
}
});
Streaming SSR:流式 SSR 传统 SSR 是服务器把整个页面都渲染完,再一次性返回 HTML。
Streaming SSR 是边渲染边返回。页面中快的部分先到浏览器,慢的部分后续补上。
适合:
- 页面模块多
- 部分接口慢
- 希望尽快让用户看到主体内容
- React 18 / Vue 3 SSR / Next.js App Router 类项目
传统 SSR:
text
请求 → 等所有数据 → 渲染完整 HTML → 返回
流式 SSR:
text
请求 → 返回页面骨架和快模块 → 慢模块 ready 后继续推送
React 示例:
tsx
import {renderToPipeableStream} from 'react-dom/server';
const stream = renderToPipeableStream(<App />, {
onShellReady() {
stream.pipe(response);
}
});
Next.js App Router 里通常通过 Suspense 使用:
tsx
import {Suspense} from 'react';
export default function Page() {
return (
<>
<Header />
<Suspense fallback={<div>Loading...</div>}>
<SlowProductList />
</Suspense>
</>
);
}
优点:
- 首字节更快
- 慢接口不阻塞整页
- 用户感知更好
缺点:
- 服务端复杂度更高
- 错误处理更复杂
- 对框架和运行环境要求更高
Islands Architecture:岛屿架构 岛屿架构的核心思想是:页面大部分内容是静态 HTML,只有需要交互的组件加载 JS。
比如一个商品详情页:
text
商品标题:静态 HTML
商品描述:静态 HTML
相关推荐:静态 HTML
收藏按钮:交互岛
规格选择器:交互岛
客服浮层:交互岛
只有「收藏按钮」「规格选择器」「客服浮层」这些需要交互的组件会变成客户端 JS。
Astro 是典型代表:
astro
---
import ProductInfo from '../components/ProductInfo.astro';
import FavoriteButton from '../components/FavoriteButton.jsx';
---
<ProductInfo />
<FavoriteButton client:load />
也可以按需激活:
astro
<FavoriteButton client:idle />
<CommentBox client:visible />
<SearchInput client:only="react" />
常见激活方式:
client:load:页面加载后立即激活client:idle:浏览器空闲时激活client:visible:组件进入视口后激活client:media:满足媒体查询后激活client:only:只在客户端渲染
优点:
- JS 体积小
- 首屏快
- SEO 好
- 很适合内容型页面加少量交互
缺点:
- 全局状态共享麻烦
- 不适合高度交互的复杂应用
- 技术栈组合要设计好
Partial Hydration:局部水合 SSR 之后,浏览器需要把静态 HTML 变成可交互页面,这个过程叫 Hydration,也就是水合。
普通 SSR 的问题是:整棵组件树都要水合。
局部水合是:只水合需要交互的部分。
它和岛屿架构很接近,但侧重点不同:
Islands更像架构模式。Partial Hydration更强调水合策略。
普通水合:
text
整个页面 HTML → 整个 App 下载 JS → 整个组件树水合
局部水合:
text
整个页面 HTML → 只给交互组件下载 JS → 只水合局部
适合:
- 内容多、交互少的页面
- 新闻、文档、商品详情
- 首屏性能敏感页面
Astro、Qwik、Marko 等框架对这类能力支持较多。
Resumability:可恢复式渲染 这是 Qwik 主推的模型。
传统 SSR 的水合成本很高,因为浏览器拿到 HTML 后,还要重新执行组件逻辑、绑定事件、恢复状态。
Resumability 的想法是:服务端渲染时把状态、事件边界、组件信息序列化到 HTML 里,客户端不需要重新跑完整应用,只在用户真正交互时恢复对应部分。
传统水合:
text
HTML 到浏览器
下载 JS
重新执行组件
绑定事件
页面可交互
可恢复式渲染:
text
HTML 到浏览器
页面已经带有恢复信息
用户点击某按钮
只加载这个按钮需要的 JS
恢复并执行
Qwik 示例:
tsx
import {component$} from '@builder.io/qwik';
export default component$(() => {
return (
<button onClick$={() => console.log('clicked')}>
Click
</button>
);
});
注意这里是 onClick$,它表示这个事件处理函数可以被懒加载和恢复。
优点:
- 极低首屏 JS
- 交互按需加载
- 对弱网、移动端友好
缺点:
- 技术心智和生态相对小众
- 需要接受框架特定写法
- 迁移成本较高
Server Components:服务端组件 Server Components 不是传统 SSR,但经常和 SSR 一起出现。
它的核心是:某些组件只在服务端执行,不打包进客户端 JS。
比如 React Server Components:
tsx
export default async function ProductList() {
const products = await db.product.findMany();
return (
<ul>
{products.map(product => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
}
这个组件:
- 可以直接访问数据库或服务端 API
- 不会进入浏览器 JS 包
- 不能使用浏览器 API
- 不能使用
useState、useEffect等客户端交互能力
如果需要交互,要拆成客户端组件:
tsx
'use client';
import {useState} from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
{count}
</button>
);
}
适合:
- 数据读取型组件
- 大量静态展示内容
- 想减少客户端 JS
- 服务端数据依赖较重的页面
优点:
- 减少客户端包体积
- 服务端数据访问更自然
- 安全性更好,敏感逻辑不下发
缺点:
- 服务端组件和客户端组件边界要清楚
- 心智成本高
- 目前主要在 React / Next.js 生态成熟
Edge Rendering:边缘渲染 Edge Rendering 是把渲染逻辑放到 CDN 边缘节点,而不是中心服务器。
传统 SSR:
text
用户 → 源站服务器 → 渲染 HTML → 返回
边缘渲染:
text
用户 → 最近的 CDN 边缘节点 → 渲染 / 拼装 HTML → 返回
适合:
- 全球用户访问
- 个性化内容
- A/B 实验
- 根据地区、设备、Cookie 做页面差异
- 对延迟敏感的页面
示例:
ts
export default async function handler(request) {
const country = request.headers.get('x-country');
if (country === 'CN') {
return new Response(renderChinaPage(), {
headers: {
'content-type': 'text/html'
}
});
}
return new Response(renderGlobalPage(), {
headers: {
'content-type': 'text/html'
}
});
}
优点:
- 延迟低
- 个性化能力强
- 可结合缓存策略
缺点:
- 边缘运行环境受限
- Node API 不一定完整
- 调试复杂
- 数据源离边缘节点远时收益会下降
怎么选 可以按页面类型来选:
text
强交互后台系统
→ CSR / CSR + 局部 SSR
内容站、文档、博客
→ SSG / Islands / Partial Hydration
商品详情、店铺页、资讯页
→ SSG + ISR / SSR + 缓存
搜索结果页、用户相关页面
→ SSR / Streaming SSR / Edge Rendering
已有 SPA 想改善 SEO
→ Prerender
全球访问、个性化入口页
→ Edge Rendering
首屏性能极致敏感且交互少
→ Islands / Resumability
更工程化一点,可以这么判断:
text
数据是否每次请求都不同?
是 → SSR / Edge Rendering
否 → SSG / ISR
页面是否需要 SEO?
是 → SSR / SSG / ISR / Prerender / Islands
否 → CSR 也可以
交互是否非常多?
是 → CSR / SSR + Hydration
否 → SSG / Islands / Partial Hydration
页面数量是否很多?
是 → ISR / SSR + 缓存
否 → SSG 可以接受
首屏 JS 是否需要极致压缩?
是 → Islands / Server Components / Resumability
结合你当前项目的语境 你这个项目里已经有 SSR 相关页面,比如移动端搜索页这类场景,通常看重:
- 首屏速度
- SEO / 抓取
- 弱网体验
- 服务端兜底
- 客户端继续接管交互
如果要在现有 SSR/CSR 之外考虑其他方案,比较现实的是:
-
Prerender适合活动页、静态营销页、规则说明页等,不太适合搜索结果这种动态页面。
-
SSG适合内容固定、构建时能拿到数据的页面。
-
ISR如果平台支持,适合商品详情、类目页、店铺页这类「数据会变,但不用秒级实时」的页面。
-
Streaming SSR适合搜索结果页、复杂聚合页,尤其当某些模块接口慢,不想阻塞整页输出。
-
Partial Hydration / Islands适合「展示内容多,交互组件少」的页面,可以显著减少客户端 JS。
一句话总结 除了 SSR 和 CSR,前端渲染还包括 SSG、ISR、Prerender、MPA、Streaming SSR、Islands、Partial Hydration、Resumability、Server Components 和 Edge Rendering。
如果按实际落地优先级看,最常用、最值得掌握的是:
text
SSG:构建时生成,适合静态内容
ISR:静态页面按周期自动刷新
Prerender:给已有 SPA 补静态 HTML
Streaming SSR:服务端边算边吐,提高首屏体验
Islands / Partial Hydration:只让局部组件带 JS,减少水合成本
Server Components:服务端执行组件,减少客户端包体积
真正做技术选型时,不要问「哪个最先进」,而要问这几个问题:页面数据是否实时、是否需要 SEO、交互复杂度高不高、页面数量大不大、能不能接受旧数据、部署平台支不支持。答案出来后,渲染方式基本也就出来了。