React Server Components(RSC)
React 的服务端组件架构,允许组件在服务端执行并将序列化的 UI 描述(RSC Payload)发送到客户端。Server Components 不下载 JS、不水合,从根本上减少了客户端 JS 量。通过 'use client' 指令划分服务端/客户端边界。
核心概念
React Server Components(RSC)重新定义了"组件在哪里执行"这个问题。传统 SSR 中,所有组件都在服务端执行一次(生成 HTML)、在客户端再执行一次(水合)。RSC 引入了一个根本性的区分:
- Server Components(默认):只在服务端执行,输出序列化的 UI 描述(RSC Payload),不发送组件的 JS 代码到客户端,不需要水合
- Client Components(
'use client'标记):在服务端预渲染 + 在客户端水合,行为与传统 React 组件一致
关键区别:RSC 的服务端组件输出的不是 HTML 字符串,而是一种可流式传输的序列化格式(RSC Payload)——本质上是 React 元素树的 JSON 化表示。客户端的 React 运行时接收这个 payload 并将其集成到组件树中,无需执行服务端组件的 JS 代码。
Server / Client 边界
// ServerComponent.jsx — 默认是 Server Component
// 可以直接 async/await、访问数据库、读文件系统
async function ArticlePage({ id }) {
const article = await db.articles.find(id); // 直接查数据库
return (
<article>
<h1>{article.title}</h1>
<p>{article.content}</p>
<CommentSection articleId={id} /> {/* Client Component */}
</article>
);
}
// CommentSection.jsx
'use client'; // 标记为 Client Component
// 可以使用 useState、useEffect、事件处理等
function CommentSection({ articleId }) {
const [comments, setComments] = useState([]);
// ...交互逻辑
}
边界规则:
- Server Component 可以导入和渲染 Client Component
- Client Component 不能导入 Server Component(但可以通过
childrenprop 接收 Server Component 的渲染结果) - 跨越边界传递的 props 必须是可序列化的(和 Qwik 的 Resumability 约束类似)
对 SSR 水合成本的影响
RSC 从根本上减少了需要水合的组件数量。一个典型的内容页面可能 80% 是 Server Components(标题、正文、导航),只有 20% 需要交互(搜索框、评论区)——水合成本从 O(所有组件) 降到 O(可交互组件)。
这和 Islands Architecture 的目标一致,但实现路径不同:
- Islands(Astro):显式用
client:*指令标记交互组件,框架无关 - RSC(Next.js):用
'use client'指令标记客户端边界,依赖 React 生态,Server/Client Components 共享同一棵 React 树
Server Actions('use server')
RSC 不仅改变了数据获取模式(Server Component 直接查数据库),还引入了 Server Actions 来处理数据变更:
// Server Action
'use server';
async function submitComment(formData) {
await db.comments.create({ text: formData.get('text') });
revalidatePath('/article');
}
Server Action 被编译为一个 RPC 端点——客户端调用时自动发送 HTTP 请求到服务端执行函数体,不需要手动写 API 路由。这是一种编译器驱动的全栈抽象——开发者写的是一个函数,编译器拆分成客户端的 fetch 调用和服务端的处理逻辑。
与 Qwik Resumability 的对比
| 维度 | RSC | Qwik Resumability |
|---|---|---|
| 核心目标 | 减少客户端 JS(部分组件不发送 JS) | 消灭水合(所有组件都不需要重新执行) |
| 交互组件的处理 | Client Components 仍需传统水合 | 事件处理函数按需懒加载,不水合 |
| 序列化约束 | 跨边界 props 必须可序列化 | 所有闭包变量必须可序列化 |
| 生态耦合 | React 专属 | Qwik 专属 |
两者可以视为同一问题的两种解法:"如何避免在客户端执行不必要的 JS?"RSC 的答案是"部分组件根本不发到客户端",Qwik 的答案是"发到客户端但不立即执行"。
引用本术语的文章