POST://react-server-components-practice
带点橘子味的馒头的头像
带点橘子味的馒头

FRONTEND / DESKTOP DEV

返回文章列表

React Server Components 实战:什么时候该用,什么时候不该用

1 分钟· 735 69 次阅读

React Server Components(RSC)上线以来争议不断:有人觉得它是未来的方向,有人觉得它让架构变复杂。作为一个把博客从纯客户端改成 RSC 的实践者,我想聊聊真实的边界在哪里。

先说结论

RSC 适合:读取数据、渲染内容、SEO 敏感、几乎无交互的页面——比如文章列表、详情页、静态页。

RSC 不适合:需要频繁交互、依赖浏览器 API、状态管理复杂的组件——比如富文本编辑器、实时表单、拖拽面板。

一个真实的取舍案例

拿我改造博客的经历说。原来文章编辑器是全客户端组件(TipTap),文章列表是纯展示。改造成 RSC 后:

// 文章详情页 —— 服务端组件 ✅
export const dynamic = "force-dynamic";

export default async function PostPage({ params }) {
  const { slug } = await params;
  const post = await getPostBySlug(slug); // 直接读文件,无 API 层
  return <ArticleContent post={post} />;
}

而编辑器里需要 useEditoronChange 的部分,用 "use client" 单独隔离:

// 编辑器 —— 客户端组件 ✅
"use client";

export default function EditorForm() {
  const editor = useEditor({ ... }); // 浏览器钩子,必须客户端
  return <EditorContent editor={editor} />;
}

边界很清晰:谁需要浏览器能力,谁就标记 client

三个关键认知

1. 序列化边界

服务端组件只能把"可序列化"的数据传给客户端组件。函数、类实例、Date 对象都不能直接传——传 params: Promise 这种就很麻烦。解法是服务端先处理好,只传纯数据:

// ❌ 不行:函数不能跨边界
<ClientComp onSave={savePost} />

// ✅ 可以:传纯数据,客户端自己处理
<ClientComp post={post} />

2. 动态渲染是显式选择

force-dynamic 之后,页面每次请求都重新执行——换来实时数据,代价是放弃静态缓存。对低流量个人站完全划算,对高流量站点要谨慎。

3. 客户端组件不是"二等公民"

RSC 的哲学是"默认服务端,按需客户端",但客户端组件依然是架构的一部分。一个常见的反模式是把整个页面标记为 client,就为了一个按钮——正确做法是把这个按钮拆出来。

容易踩的坑

表现 解法
use client 传染 一个交互组件拖全家变 client 交互能力下沉到叶子组件
hydration 不一致 服务端和客户端渲染结果不同 检查是否依赖 window/localStorage
参数类型 paramsPromise,直接访问报错 const { slug } = await params
静态误判 页面被缓存,数据不更新 显式加 dynamicrevalidate

小结

RSC 的正确打开方式不是"所有页面都用",而是按交互边界划分

  • 读数据的页面 → 服务端组件,白嫖渲染性能;
  • 有交互的组件 → 客户端组件,隔离边界;
  • 明确数据新鲜度需求 → 显式 dynamic

架构没有银弹,但 RSC 给了我们一个更清晰的取舍工具。

相关文章