React Server Components(RSC)初学时最别扭的一点是:它不像「又一个生命周期」,而像换了一种组件运行时。服务端组件可以碰数据库、读文件;但一旦跨过边界,传给客户端的只能是可序列化的数据。
先记住一条边界
- Server Component:默认,无
useState/useEffect,可 async。 - Client Component:文件顶注明
"use client",可交互,收 props 必须可序列化。
// app/posts/page.tsx —— Server Component
import { getPosts } from "@/lib/content";
import { LikeButton } from "./like-button"; // Client
export default async function Page() {
const posts = await getPosts();
return (
<ul>
{posts.map((p) => (
<li key={p.slug}>
{p.title}
<LikeButton slug={p.slug} />
</li>
))}
</ul>
);
}客户端组件该放哪
把交互沉到叶子节点,而不是一上来把整页标成 "use client"。
| 放服务端 | 放客户端 |
|---|---|
| 数据获取、权限判断 | 输入框、弹层、拖拽 |
| Markdown 渲染(静态) | 主题切换、本地搜索 |
| 布局与 SEO 文本 | 依赖浏览器 API 的逻辑 |
序列化坑
不能把函数、class 实例、Date(视传输层而定)、Map/Set 直接当 props 塞给客户端组件。更稳妥的做法是传 plain object / string / number,日期用 ISO 字符串。
常见误解
- 「RSC 替代了 SPA」——没有,交互面仍需要客户端 JS。
- 「有了 RSC 就不需要 API」——对内可以直查数据库;对外部客户端、移动端,API 仍在。
小结
画边界时问两句:这段逻辑是否必须碰浏览器?传过去的数据能否 JSON 化?答完,"use client" 该写在哪通常就清楚了。