一、什么是 React Server Components?
React Server Components(RSC)是 React 团队提出的革命性特性,它允许组件在服务端渲染,且不会向客户端发送任何 JavaScript 代码。这与传统的 SSR(服务端渲染)有着本质区别。
一句话理解:
-
SSR:在服务端生成 HTML,但组件 JS 仍需下载执行(hydration)
-
RSC:组件只在服务端运行,零客户端 JS 负担
┌─────────────────────────────────────────────────────────┐
│ 传统 SSR │
│ Server: 渲染 HTML ──────→ Client: 下载 JS + Hydration │
│ │
│ React Server Components │
│ Server: 渲染 + 执行组件 ──→ Client: 只接收序列化数据 │
│ (JS 不会发送到浏览器) │
└─────────────────────────────────────────────────────────┘
二、核心原理剖析
2.1 RSC Payload 格式
RSC 不会返回 HTML,而是返回一种特殊的流式 JSON 格式:
// 这是简化后的 RSC Payload 示意
{
"type": "div",
"props": {
"children": [
{
"type": "$L1", // 指向懒加载的 Client Component
"props": { "title": "Hello RSC" }
},
{
"type": "p",
"props": { "children": "这段文字来自服务端" }
}
]
}
}
2.2 服务端组件 vs 客户端组件
| 特性 | Server Component | Client Component |
|---|---|---|
| 运行环境 | 服务端 | 浏览器 |
| 访问后端资源 | ✅ 直接访问 | ❌ 需通过 API |
| 包体积影响 | 零 JS 开销 | 正常打包 |
| 使用 hooks | ❌ 不支持 | ✅ 支持 |
| 使用浏览器 API | ❌ 不支持 | ✅ 支持 |
三、实战:从零搭建 RSC 应用
3.1 Next.js 14 App Router 配置
// app/page.tsx - 默认就是 Server Component
import { db } from '@/lib/db';
import { UserCard } from './UserCard';
// ✅ 可以直接访问数据库!
async function getUsers() {
return await db.query('SELECT * FROM users LIMIT 10');
}
export default async function HomePage() {
const users = await getUsers(); // 在服务端执行
return (
<main>
<h1>用户列表</h1>
{users.map(user => (
<UserCard key={user.id} user={user} />
))}
</main>
);
}
// app/UserCard.tsx - 这是一个 Client Component
'use client'; // 标记为客户端组件
import { useState } from 'react';
export function UserCard({ user }) {
const [expanded, setExpanded] = useState(false);
return (
<div
className="user-card"
onClick={() => setExpanded(!expanded)}
>
<h3>{user.name}</h3>
{expanded && <p>{user.bio}</p>}
</div>
);
}
3.2 组件组合模式
// app/PostList.tsx - Server Component
import { getPosts } from '@/lib/api';
import { LikeButton } from './LikeButton'; // Client Component
export async function PostList() {
const posts = await getPosts();
return (
<ul>
{posts.map(post => (
<li key={post.id}>
<h2>{post.title}</h2>
<p>{post.content}</p>
{/* 客户端组件作为服务端组件的子组件 */}
<LikeButton postId={post.id} initialLikes={post.likes} />
</li>
))}
</ul>
);
}
// app/LikeButton.tsx - Client Component
'use client';
import { useOptimistic } from 'react';
import { likePost } from './actions';
export function LikeButton({ postId, initialLikes }) {
const [optimisticLikes, addOptimisticLike] = useOptimistic(
initialLikes,
(state) => state + 1
);
const handleLike = async () => {
addOptimisticLike(undefined);
await likePost(postId);
};
return (
<button onClick={handleLike}>
❤️ {optimisticLikes}
</button>
);
}
3.3 Server Actions:无缝的后端调用
// app/actions.ts
'use server'; // 标记为 Server Action
import { revalidatePath } from 'next/cache';
import { db } from '@/lib/db';
export async function likePost(postId: number) {
await db.query('UPDATE posts SET likes = likes + 1 WHERE id = ?', [postId]);
// 刷新缓存,自动更新页面
revalidatePath('/posts');
}
四、性能对比实测
我在一个电商项目中对比了传统 CSR 和 RSC 架构:
| 指标 | CSR (React Query) | RSC (Next.js 14) | 提升 |
|---|---|---|---|
| 首屏 JS 体积 | 285 KB | 78 KB | 72.6% |
| TTI (可交互时间) | 2.8s | 1.4s | 50% |
| 首次数据获取 | 客户端瀑布请求 | 服务端并行查询 | 80% |
| Lighthouse 性能分 | 72 | 96 | +24 |
五、常见陷阱与最佳实践
❌ 错误示范:在 Server Component 中使用 hooks
// ❌ 会报错!
import { useState } from 'react';
export default function ServerComponent() {
const [count, setCount] = useState(0); // Error!
return <div>{count}</div>;
}
✅ 正确做法:分离服务端和客户端逻辑
// SearchResults.tsx - Server Component
import { searchProducts } from '@/lib/db';
import { SearchFilter } from './SearchFilter'; // Client
export async function SearchResults({ query }) {
const products = await searchProducts(query);
return (
<>
<SearchFilter /> {/* 客户端:交互逻辑 */}
<ProductGrid products={products} /> {/* 服务端:数据展示 */}
</>
);
}
最佳实践清单
- 默认用 Server Component,除非需要客户端交互
- 数据获取尽量放在服务端,减少客户端瀑布请求
- 用 Server Actions 处理表单提交,替代传统 API 路由
- 缓存策略 :
unstable_cache或revalidatePath精细控制
六、未来展望
React Server Components 不只是 Next.js 的专利,未来会有更多框架支持:
- Remix 正在集成 RSC
- React Router v7 计划支持
- Waku - 纯 RSC 框架,值得关注
七、总结
RSC 重新定义了前后端边界,让数据获取更接近数据源,同时保持组件化的开发体验。虽然学习曲线存在,但带来的性能收益是实实在在的。
💡 适用场景: 内容型网站、数据密集型应用、SEO 敏感项目
⚠️ 不适用: 重度交互应用(如 Figma、在线 IDE)仍需以客户端为主