React Server Components(RSC)是 React 对组件渲染模型的一次本质性拓展。传统 React 组件全部在客户端渲染(CSR),而 RSC 允许组件在服务端执行并渲染,最终将序列化的 UI 结构发送给客户端。这种分化带来了全新的组件协作模式,理解和区分“服务端组件”与“客户端组件”是掌握 RSC 的关键。
什么是服务端组件
服务端组件(Server Component)是在 Node.js 等服务器环境中运行的 React 组件。它们永远不会下载到客户端,也不会参与浏览器里的重渲染。服务端组件可以:
- 直接访问服务端资源:数据库、文件系统、微服务接口。
- 使用后端 SDK 和敏感密钥,因为没有泄露风险。
- 返回 JSX(或序列化的 UI 描述),由 React 流式传输给客户端。
- 没有交互能力:不能使用
useState、useEffect等仅在浏览器端可用的 Hooks。
服务端组件只是一个纯函数,接收 Props,返回 UI 描述,并支持 async/await,可以直接在组件内写 await fetch(...) 而不需要 useEffect。
// 服务端组件,可直接异步请求数据
export default async function ProductList() {
const products = await db.product.findMany(); // 直接查数据库
return (
<ul>
{products.map(p => (
<li key={p.id}>{p.name}</li>
))}
</ul>
);
}
什么是客户端组件
客户端组件(Client Component)即我们熟悉的传统 React 组件,在浏览器中运行并进行交互。它们被完整地打包并发送到客户端,使用标准的 Hooks 和浏览器 API。在文件顶部添加 'use client' 指令来标记。
'use client';
import { useState } from 'react';
export default function LikeButton() {
const [liked, setLiked] = useState(false);
return (
<button onClick={() => setLiked(!liked)}>
{liked ? '❤️' : '🤍'}
</button>
);
}
'use client' 标记一个边界:该文件及其所有依赖都将属于客户端包。因此,你需要谨慎决定边界位置。
核心差异对比
| 特性 | 服务端组件 | 客户端组件 |
|---------------------|-------------------------------|-------------------------------|
| 执行环境 | 服务器(Node.js) | 浏览器 |
| 交互能力 | 无,不能使用事件处理或 Hooks | 有,可以使用所有浏览器 API 和 Hooks |
| 数据请求 | 组件内直接 async/await | 需要 useEffect 或数据请求库 |
| 打包结果 | 不会被发送到客户端,零 JavaScript 体积 | 完整代码发送到客户端 |
| 访问后端资源 | 可以,安全 | 不可直接,需通过 API |
| 标记方式 | 默认都是服务端组件(视框架而定) | 文件顶部添加 'use client' |
| 渲染时机 | 每次请求在服务端运行 | 客户端首次加载和后续重渲染 |
两者如何协作
RSC 的精髓在于组合:一个页面由服务端组件树构成,其中某些节点可以是客户端组件。服务端组件负责获取数据和生成静态 UI,在需要交互的地方嵌入客户端组件作为“岛屿”。
// 服务端组件
import LikeButton from './LikeButton'; // 客户端组件
import db from '@/lib/db';
export default async function Page() {
const post = await db.post.findFirst();
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
{/* 交互部分交给客户端组件 */}
<LikeButton />
</article>
);
}
客户端组件的 Props 由服务端组件序列化传递,但要求 Props 是可序列化的数据(不能是函数、类实例等)。React 在服务端完成序列化后,发送给客户端的是一段特殊的 RSC 负载(React Flight 协议),客户端将其重构为虚拟 DOM,并与客户端组件混合渲染。
选择原则
- 默认使用服务端组件:因为零客户端体积、安全和性能优势。
- 当需要交互或浏览器 API 时,再添加客户端组件:如事件处理、浏览器状态、
useEffect、仅浏览器 Hooks。 - 将客户端组件推近交互的叶子节点,而不是整棵树标记为客户端,以最大化服务端渲染优势。
常见误区
- ❌ “服务端组件就是 SSR”。SSR 将组件渲染为 HTML 字符串发给浏览器,但所有组件仍然会发送给客户端并执行(水合)。RSC 中,服务端组件完全不进入客户端包,不存在水合。
- ❌ “
'use client'意味着整个文件都在客户端运行”。其实它标记了一个边界,该文件引用的其他组件如果也是客户端组件才在客户端运行,它本身被作为边界入口,其自身的渲染依然可能在服务端完成(生成序列化后的占位指令),但组件代码会发送到客户端进行水合和后续交互。 - ❌ “服务端组件不能有状态”。是的,它们没有客户端状态,但可以通过 Props 接收来自客户端状态的数据(通过提升状态到客户端组件)。
掌握了服务端和客户端组件的区别,你就能设计出性能极佳、代码精简的现代 React 应用。React Server Components 将“服务器能力”直接交到组件手中,让前端开发者能够像写普通 UI 那样去安全、高效地使用后端数据。