SSR/CSR/SSG渲染策略对比
约 2094 字大约 7 分钟
ssrcsrssg
2025-08-05
概述
现代 Web 应用有多种渲染策略可供选择,每种策略在首屏性能、SEO、开发复杂度和服务器负载之间有不同的权衡。理解各种渲染策略的工作原理和适用场景,是做出正确架构决策的基础。
渲染策略总览
1. CSR(Client-Side Rendering)
浏览器下载空 HTML 壳,由 JavaScript 在客户端动态渲染全部内容。
<!-- CSR 的 HTML -->
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<div id="root"></div> <!-- 空壳 -->
<script src="/bundle.js"></script>
</body>
</html>// React CSR 入口
import { createRoot } from 'react-dom/client';
import App from './App';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
// 数据在组件中获取
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('/api/users')
.then(res => res.json())
.then(setUsers);
}, []);
return users.map(u => <div key={u.id}>{u.name}</div>);
}2. SSR(Server-Side Rendering)
服务器为每个请求生成完整的 HTML,浏览器收到后立即展示内容,然后通过 Hydration 恢复交互能力。
// Next.js SSR(App Router)
// app/users/page.tsx
async function UsersPage() {
// 在服务端获取数据
const users = await fetch('https://api.example.com/users', {
cache: 'no-store' // 每次请求都获取最新数据
}).then(res => res.json());
return (
<div>
<h1>Users</h1>
{users.map(user => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}
export default UsersPage;// Express + React SSR 手动实现
import express from 'express';
import { renderToString } from 'react-dom/server';
import App from './App';
const app = express();
app.get('*', async (req, res) => {
// 服务端获取数据
const data = await fetchData(req.url);
// 渲染 HTML
const html = renderToString(<App data={data} url={req.url} />);
res.send(`
<!DOCTYPE html>
<html>
<head><title>SSR App</title></head>
<body>
<div id="root">${html}</div>
<script>
window.__INITIAL_DATA__ = ${JSON.stringify(data)}
</script>
<script src="/bundle.js"></script>
</body>
</html>
`);
});3. SSG(Static Site Generation)
在构建时预生成所有页面的静态 HTML,部署到 CDN,不需要运行时服务器。
// Next.js SSG
// app/posts/[slug]/page.tsx
// 告诉 Next.js 需要生成哪些页面
export async function generateStaticParams() {
const posts = await fetch('https://api.example.com/posts').then(r => r.json());
return posts.map(post => ({ slug: post.slug }));
}
// 构建时获取数据
async function PostPage({ params }) {
const post = await fetch(
`https://api.example.com/posts/${params.slug}`,
{ cache: 'force-cache' } // 构建时缓存
).then(r => r.json());
return (
<article>
<h1>{post.title}</h1>
<PostContent html={post.content} />
</article>
);
}
export default PostPage;4. ISR(Incremental Static Regeneration)
ISR 结合了 SSG 的性能和 SSR 的数据新鲜度:静态页面定期在后台重新生成。
// Next.js ISR
async function ProductPage({ params }) {
const product = await fetch(
`https://api.example.com/products/${params.id}`,
{ next: { revalidate: 60 } } // 60 秒后重新验证
).then(r => r.json());
return <ProductDetail product={product} />;
}
// On-demand Revalidation(按需重新生成)
// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from 'next/cache';
export async function POST(request) {
const { path, tag } = await request.json();
if (path) {
revalidatePath(path); // 重新生成指定路径
}
if (tag) {
revalidateTag(tag); // 重新生成带指定 tag 的页面
}
return Response.json({ revalidated: true });
}5. Streaming SSR(流式渲染)
不再等待所有数据准备好才发送 HTML,而是边生成边发送,配合 Suspense 实现渐进式渲染。
// React 18 + Streaming SSR
// Next.js App Router 默认支持
import { Suspense } from 'react';
export default function Dashboard() {
return (
<div>
<nav>Dashboard Navigation</nav> {/* 立即发送 */}
<Suspense fallback={<UserSkeleton />}>
<UserProfile /> {/* 数据就绪后流式发送 */}
</Suspense>
<Suspense fallback={<ChartSkeleton />}>
<Analytics /> {/* 独立的数据流 */}
</Suspense>
<Suspense fallback={<ListSkeleton />}>
<ActivityFeed /> {/* 独立的数据流 */}
</Suspense>
</div>
);
}
// 每个组件独立获取数据
async function UserProfile() {
const user = await fetchUser(); // 可能需要 200ms
return <div>{user.name}</div>;
}
async function Analytics() {
const data = await fetchAnalytics(); // 可能需要 2000ms
return <Chart data={data} />;
}6. React Server Components(RSC)
RSC 在服务端渲染组件,不发送组件 JavaScript 到客户端,大幅减少 Bundle 大小。
// Server Component(默认)— 零 JS Bundle
// app/posts/page.tsx
import { db } from '@/lib/database';
export default async function PostsPage() {
// 直接查询数据库,不暴露给客户端
const posts = await db.post.findMany({
orderBy: { createdAt: 'desc' }
});
return (
<div>
<h1>Posts</h1>
{posts.map(post => (
<PostCard key={post.id} post={post} />
))}
<LikeButton /> {/* Client Component */}
</div>
);
}
// Client Component — 发送 JS 到客户端
// components/LikeButton.tsx
'use client'; // 标记为客户端组件
import { useState } from 'react';
export function LikeButton() {
const [liked, setLiked] = useState(false);
return (
<button onClick={() => setLiked(!liked)}>
{liked ? 'Liked' : 'Like'}
</button>
);
}7. Hydration(注水)
Hydration 是将服务端生成的静态 HTML 与客户端 JavaScript 关联的过程。
// 传统 Hydration
import { hydrateRoot } from 'react-dom/client';
// 服务端渲染的 HTML 已在 DOM 中
const root = hydrateRoot(
document.getElementById('root'),
<App initialData={window.__INITIAL_DATA__} />
);
// Hydration 不重新创建 DOM,而是复用现有 DOM 并注入事件
// 如果客户端和服务端渲染结果不匹配,会报 Hydration Mismatch 警告8. 策略对比总表
| 特性 | CSR | SSR | SSG | ISR | Streaming SSR |
|---|---|---|---|---|---|
| FCP 速度 | 慢 | 快 | 最快 | 最快 | 快 |
| TTI 速度 | 依赖 JS 大小 | 中等 | 快 | 快 | 渐进式 |
| SEO | 差 | 好 | 最好 | 好 | 好 |
| 数据新鲜度 | 实时 | 实时 | 构建时 | 定期更新 | 实时 |
| 服务器负载 | 无 | 高(每请求渲染) | 无(CDN) | 低 | 中等 |
| 构建时间 | 快 | 快 | 页面多时慢 | 增量构建 | 快 |
| 适合页面数 | 任意 | 任意 | 有限 | 大量 | 任意 |
选择决策树
混合渲染策略
现代框架(如 Next.js)支持在同一应用中混合使用不同策略:
/ → SSG(首页,内容固定)
/blog → SSG(博客列表)
/blog/[slug] → ISR(博客详情,60s 重新生成)
/products → ISR(产品列表,300s 重新生成)
/products/[id] → SSR(产品详情,需实时库存)
/dashboard → CSR(后台面板,无 SEO 需求)
/feed → Streaming SSR(社交 Feed,渐进加载)总结
渲染策略的选择没有银弹,需要根据页面的 SEO 需求、数据更新频率、个性化程度和性能要求做出权衡。SSG 提供最佳性能但数据是静态的;SSR 保证数据新鲜但增加服务器压力;CSR 开发简单但首屏慢、SEO 差;ISR 结合了 SSG 和 SSR 的优势;Streaming SSR 和 RSC 是最新的渲染演进,进一步优化了用户体验。现代框架支持在同一应用中按页面选择不同策略,开发者应当根据每个页面的特点做出最优选择。
贡献者
更新日志
2026/3/14 13:09
查看所有更新日志
9f6c2-feat: organize wiki content and refresh site setup于