4 个英文缩写
| 策略 | 全名 | 一句话 |
|---|---|---|
| CSR | Client-Side Rendering | 浏览器拿空 HTML + JS bundle,JS 跑完才出内容 |
| SSR | Server-Side Rendering | 服务器每次请求渲染 HTML 返回 |
| SSG | Static Site Generation | 构建时一次性预渲染 HTML,请求时直接发文件 |
| ISR | Incremental Static Regen | SSG + 后台按需更新(Next.js 的招牌) |
CSR:经典 SPA
// Vite + React Router 的 index.html
<div id="root"></div>
<script src="/bundle.js"></script>
优点:
- 部署简单(任意 CDN 扔静态文件)
- 切换路由无白屏
- 后端只管 API
缺点:
- 首屏白
- SEO 差(爬虫看到空 div)
- 弱设备 / 弱网解析 JS 慢
适用:
- 内部工具 / 后台
- 登录后看到的 dashboard
- SEO 不重要
SSR:每次请求渲染
// Next.js Pages Router 老法
export async function getServerSideProps() {
const data = await fetch(...);
return { props: { data } };
}
App Router 新法:
// 默认每次请求渲染
export default async function Page() {
const data = await fetch('https://api/...', { cache: 'no-store' });
return <div>{data.x}</div>;
}
优点:
- 首屏直接出 HTML(快、SEO 友好)
- 数据永远最新
缺点:
- 每次请求都要服务器算(CPU 成本)
- 慢接口拖累 TTFB
适用:
- 内容随时变(个性化 dashboard、实时数据)
- 必须最新(订单详情、库存)
SSG:构建时预渲染
// Next.js 15+:fetch 默认不缓存。要 SSG 必须显式 force-cache(或加 revalidate)
export default async function Page() {
const posts = await fetch('https://api/posts', { cache: 'force-cache' }).then(r => r.json());
return <PostList posts={posts} />;
}
// 或者整页声明
export const dynamic = 'force-static';
优点:
- 极快(HTML 已经在 CDN)
- 0 服务器算力
- 极便宜
缺点:
- 数据更新要重新构建
- 大量页面构建慢(10w 篇博客构建 30 分钟)
适用:
- 博客 / 文档 / 营销页
- 内容更新不频繁
ISR:SSG + 后台刷新
// 这条路由:构建时预渲染,但每 60 秒后台请求时重新生成
export default async function Page() {
const data = await fetch('https://api/...', { next: { revalidate: 60 } });
return ...;
}
或者基于事件按需更新:
// 在 Server Action 或 API 路由里
import { revalidatePath, revalidateTag } from 'next/cache';
revalidatePath('/posts/[id]', 'page');
优点:SSG 的速度 + SSR 的新鲜度 缺点:心智模型复杂、调试 cache 烦
适用:电商列表 / 价格 / 排行榜 / 半实时内容
混合:一个网站多种共存
首页 marketing → SSG
博客文章详情 → SSG
登录后 dashboard → CSR(或 SSR if SEO 需要)
产品列表(价格变) → ISR
订单详情 → SSR(必须最新)
搜索结果 → CSR(用户行为驱动)
Next.js 让一个项目里不同路由用不同策略——你不用全押在一种上。
RSC 改变了什么
React Server Components不替代上面 4 种,它是上面 4 种的实现方式之一。Next.js 15+ 的默认是 SSR / 动态渲染,你需要显式 opt-in 静态:
cache: 'force-cache'或export const dynamic = 'force-static'→ SSGcache: 'no-store'或export const dynamic = 'force-dynamic'(默认) → SSRnext: { revalidate: N }→ ISRnext: { tags: [...] }+revalidateTag()→ 按需 ISR
RSC 真正的新东西是:部分组件在服务器、部分在客户端、bundle 更小。
Hydration(水合)
SSR/SSG 的 HTML 到了浏览器,JS 还得跑一遍给 HTML 接事件——这叫 hydration。
如果服务器渲染的 HTML 和客户端首次 render 结果不一致,React 报警 "hydration mismatch"。常见原因:
function NavBar() {
return <p>{new Date().toLocaleString()}</p>; // 服务器和客户端时间不一样 → mismatch
}
修:
- 把时间逻辑放进
useEffect(客户端跑) - 或用
<time suppressHydrationWarning>...</time>(白名单这一处)
选哪种?决策表
| 你的需求 | 选 |
|---|---|
| SEO 重要 + 内容稳定 | SSG |
| SEO 重要 + 内容半实时 | ISR |
| SEO 重要 + 内容实时 / 个性化 | SSR |
| SEO 不重要 + 内部工具 | CSR |
| 部分页 SEO 重要、部分不重要 | 混合(Next.js 默认就支持) |
部署影响
- CSR:纯静态托管(CDN / S3 / Pages)
- SSG:同上
- ISR:需要 Node / Edge 运行时(Vercel / 自建 Node 服务器)
- SSR:同上
→ 下一篇 生产部署