在 React 19.2 中,React 的架构发生了根本性的转变,推行了 Server-First(服务端优先) 的理念。Server Components(服务端组件) 和 Server Functions(服务端函数) 是支撑这一现代架构的两大核心支柱。
虽然它们都带有 "Server" 字样,但各自的分工截然不同:Server Components 负责"由内向外"的数据渲染,而 Server Functions 负责"由外向内"的行为交互。
一、 React Server Components (RSC) ------ 服务端组件
1. 什么是 RSC?
React Server Components 是一种全新类型的组件,它完全在服务端运行 (可以在构建时运行,也可以在每次请求时运行)。RSC 的最大特点是:它的源代码和依赖永远不会被发送到浏览器的客户端。
2. 核心优势
- 零客户端体积(Zero Bundle Size): 如果你在 RSC 中使用了大型第三方库(如
marked渲染 Markdown 或sanitize-html过滤 HTML),这些库只留在服务端,客户端不需要下载它们。 - 直接访问后端资源: 你可以直接在组件内部编写数据库查询、读取文件系统或调用内部微服务,无需再写一套
/api/路由。 - 更佳的初始加载与配置: 相比传统 SSR 仅能生成初始 HTML,RSC 还能生成一种特殊的流式数据(RSC Payload),允许客户端在不丢失当前 UI 状态的情况下,平滑地融合服务端更新。
- 19.2 新特性增强: 在 React 19.2 中,React 优化了服务端渲染的 Suspense 边界合并流式处理(Batching Reveals)。以往流式传输时,服务端渲染的 Suspense 内容会零散地替代占位符,而 19.2 开始,React 会在短时间内批量合并显示,让首屏加载的视觉体验更丝滑、不闪烁。
3. 代码示例
tsx
// 默认情况下(在现代支持 RSC 的框架如 Next.js 中),组件都是 Server Component
import { db } from '@/lib/db';
import marked from 'marked'; // 这个库的 JS 不会被下载到客户端
interface ArticlePageProps {
params: { id: string };
}
export default async function ArticlePage({ params }: ArticlePageProps) {
// 直接在组件内进行异步数据库查询
const article = await db.article.findUnique({ where: { id: params.id } });
if (!article) return <div>文章不存在</div>;
const htmlContent = marked(article.content);
return (
<article className="prose">
<h1>{article.title}</h1>
{/* 直接渲染渲染好的 HTML */}
<div dangerouslySetInnerHTML={{ __html: htmlContent }} />
</article>
);
}
二、 Server Functions ------ 服务端函数
1. 什么是 Server Functions?
Server Functions(在表单场景下常被称为 Server Actions )是通过 "use server" 指令标记的异步函数 。它们允许客户端代码像调用本地函数一样,直接调用运行在服务端的代码。
本质上,React 在幕后为你自动构建了一个安全、类型安全的 RPC(远程过程调用) 桥梁。
2. 核心优势
- 告别繁琐的 API 路由: 以前为了提交一个表单,你需要写
fetch('/api/submit', { method: 'POST' })并手动处理序列化。现在只需一个普通的异步函数。 - 渐进式增强(Progressive Enhancement): 当与
<form action={serverFunction}>结合使用时,即使客户端的 JavaScript 尚未加载或下载完毕,用户点击提交,表单依然可以通过传统的 HTTP POST 请求正常工作。 - 类型安全: 从客户端传递给 Server Function 的参数在编译时是端到端类型安全的。
3. 代码示例
tsx
// actions.ts ------ 声明服务端函数
'use server'; // 必须放在文件顶部
import { db } from '@/lib/db';
import { revalidatePath } from 'next/cache';
export async function createComment(formData: FormData) {
const content = formData.get('commentContent') as string;
const articleId = formData.get('articleId') as string;
if (!content) return { error: '内容不能为空' };
// 1. 安全地在服务端操作数据库
await db.comment.create({
data: { content, articleId }
});
// 2. 清除缓存,触发相关页面在服务端重新渲染(RSC 刷新)
revalidatePath(`/article/${articleId}`);
return { success: true };
}
在客户端组件(Client Component)中调用:
tsx
// CommentForm.tsx
'use client'; // 客户端组件
import { createComment } from './actions';
export default function CommentForm({ articleId }: { articleId: string }) {
return (
<form action={createComment}>
<input type="hidden" name="articleId" value={articleId} />
<textarea name="commentContent" placeholder="说说你的看法..." />
<button type="submit">提交评论</button>
</form>
);
}
三、 二者的协同工作模型
在 React 19.2 的全栈应用中,它们两者不是孤立的,而是完美闭环的。
- RSC(服务端组件) 在服务端获取数据,并将基础结构渲染好。
- 如果页面有交互(如点赞、评论),RSC 会将 Server Functions 作为
props传递给 Client Components(客户端组件)。 - 用户在浏览器端触发点击,Client Component 调用该 Server Function。
- 请求飞往服务器执行,服务器更新数据库后,重新计算并生成受影响的 RSC 视图,最后将全新的 UI 片段(RSC Payload)流式发回浏览器。
- 客户端 React 悄无声息地局部刷新 DOM,用户甚至感觉不到页面刷新。
四、 核心区别对比
| 特性 | React Server Components (RSC) | Server Functions ('use server') |
|---|---|---|
| 主要定位 | UI 渲染 与数据读取 | 行为交互 与数据变更 (Mutations) |
| 执行时机 | 页面加载、路由切换时在服务端运行 | 用户在客户端触发事件(如点击、提交)时运行 |
| 返回值 | 返回 React 渲染树/UI 片段 | 返回普通数据(JSON 序列化对象、Promise、FormData) |
| 文件指令 | 默认即是(或在部分架构中无需特殊指令) | 必须在顶部使用 'use server' 指令 |
| 客户端体积 | 依赖库 0 字节 传给客户端 | 函数本身转化为一个微小的 URL 请求代理 |
💡 避坑提示(安全与限制)
- 参数必须可序列化: 由于 Server Functions 需要跨越网络传输,你不能传递不可序列化的数据(例如实时的 DOM 节点、复杂的 class 实例或普通的客户端函数)。
- 防范越权: 即使 Server Functions 隐藏了 API 路径,它在幕后依然是个网络接口。在 React 19.2 中编写 Server Functions 时,切记要在函数内部重新校验用户的 Session 和权限 ,绝不能盲目信任客户端传过来的参数(例如
userId)。