一、缘起:为什么需要 Server Components?
2020 年底,React 团队在 RFC 中首次提出了 Server Components 的概念。 当时社区一片哗然——"React 不是客户端框架吗?服务端渲染(SSR)不是已经有 Next.js 做了吗?"
答案藏在两个核心痛点里:
- 瀑布式数据请求。 传统的 CSR 应用中,组件树渲染 → 发现数据依赖 → 发起请求 → 等待响应,层层递进,像多米诺骨牌一样依次倒下。
- 客户端 JS 体积膨胀。 日期格式化库、Markdown 解析器、语法高亮…… 这些"纯函数"般的依赖占据了最终 bundle 的 40% 以上,但它们本就不需要 在浏览器里运行。
二、核心心智模型
RSC 的精髓在于一个简单的二元划分:
| 类型 | 运行环境 | 能力 |
|---|---|---|
| Server Component(默认) | 服务端 | 直接访问数据库/文件系统、import 重型库、Node.js API |
| Client Component | 浏览器 | useState、useEffect、事件处理、浏览器 API |
// ✅ server-component.tsx —— 默认就是服务端组件
import { db } from "@/lib/db";
export default async function PostList() {
const posts = await db.post.findMany(); // 直接查数据库,零客户端 JS!
return (
<ul>
{posts.map(p => <li key={p.id}>{p.title}</li>)}
</ul>
);
}
// ✅ client-component.tsx —— 'use client' 边界标记
'use client';
import { useState } from 'react';
export default function LikeButton({ postId }: { postId: string }) {
const [liked, setLiked] = useState(false);
return (
<button onClick={() => setLiked(!liked)}>
{liked ? '❤️' : '🤍'}
</button>
);
}
三、实践中的三个关键决策
1. 服务端组件 vs 客户端组件的边界在哪里?
我的原则:叶子节点优先客户端,数据流优先服务端。
- 列表容器 → 服务端(数据获取)
- 列表项交互按钮 → 客户端(事件处理)
- 评论区展示 → 服务端(首屏渲染)
- 评论输入框 → 客户端(表单交互)
2. 如何优雅地传递数据?
避免 prop drilling 的最佳实践:在服务端组件中完成所有数据获取, 通过 props 向下传递给客户端组件。服务端组件可以嵌套客户端组件, 但反过来不行——'use client' 是单向边界。
3. 第三方库的兼容性
大部分 UI 库(如 Radix UI、Headless UI)已经全面支持 RSC。
对于尚未适配的库(如某些图表库),使用 dynamic(() => import(...), { ssr: false })
包裹即可。
四、总结
RSC 不是对现有模式的推翻,而是补充。它让 React 从"一刀切的客户端渲染" 进化为"在组件粒度上自由选择运行时"。这是一种对开发者友好的渐进式变革—— 你可以从一个组件开始,逐步将数据获取逻辑迁移到服务端,而不需要重写整个应用。
最好的架构,是让正确的代码运行在正确的地方。