Skip to main content

server component 的整个渲染过程是怎么样的?在这个渲染过程中 server component 和 client component 有什么区别

React Server Component 的整个渲染过程​

React Server Components(RSC)将组件分为两类:Server Component(只在服务器端运行)和 Client Component(在客户端运行)。整个渲染过程可以概括为:服务器端生成 RSC 数据流 → 客户端接收并重建组件树 → 挂载/水合 Client Component。

下面是完整流程(以典型的 React 18 + RSC 实现为例,不包含 Next.js 等框架的附加 SSR 阶段):

1. 服务器端渲染阶段​

1.1 请求触发
客户端发起页面请求(如初次加载或导航到新路由)。服务器收到请求后,开始渲染根组件(通常是一个 Server Component)。

1.2 递归渲染 Server Component
服务器依次执行每个 Server Component 的代码:

  • Server Component 可以直接使用 async/await 读取数据库、文件系统或调用内部服务。
  • 渲染过程中,如果遇到 另一个 Server Component,继续在服务器端执行它。
  • 如果遇到 Client Component,服务器不会执行它的函数体,而是:
    • 生成一个占位符节点,记录该 Client Component 的模块引用(id)和可序列化的 props。
    • 将子元素(可能继续包含 Server/Client Component)递归处理。

1.3 序列化为 RSC Payload
整个组件树最终被序列化为一种特殊的流式格式(RSC Payload),例如:

1:I{"id":"./src/ClientButton.js","chunks":["client-button"]}
2:{"name":"John"}
3:$
...
  • Server Component 的输出被“压平”为原始 React 元素(如 <div>hello</div>)以及文本节点。
  • Client Component 被编码为带有模块 ID 的引用,并附上其 props(必须可序列化)。
  • 不包含任何实际的 HTML 字符串,而是一种描述性的数据结构。

1.4 发送给客户端
服务器将 RSC Payload 发送到客户端(可通过 HTTP 或流式传输)。

2. 客户端重建阶段​

2.1 解析 Payload
客户端接收到 RSC Payload 后,React 反序列化并构建一个虚拟 React 元素树。

2.2 动态加载 Client Component 模块
当虚拟树中遇到一个 Client Component 的引用时,React 会:

  • 根据模块 ID 动态导入对应的 JavaScript 包(如果尚未加载)。
  • 将 props 传入该 Client Component。

2.3 最终渲染

  • Server Component 部分(已经是纯 React 元素)被直接放入渲染树,不会在客户端再次“执行”组件函数。
  • Client Component 在客户端正常执行(调用其函数体),生成 DOM 并挂载。
  • 如果 Client Component 包含交互(如 onClick),后续还需要进行水合(Hydration),将事件监听器附加到已有的 DOM 节点上(但这一步通常和传统的 SSR 水合不同,RSC 中 Client Component 可能是动态插入的,水合由框架负责)。

3. 后续更新(客户端导航)​

当用户在客户端导航(如点击 <Link>)到另一个 RSC 路由时:

  • 不会刷新整个页面。
  • 客户端向服务器发送请求,获取新页面的 RSC Payload。
  • 服务器重新执行 Server Component(重新读取数据库等),生成新 Payload。
  • 客户端合并新 Payload 与现有 Client Component,仅更新变化的 DOM。

Server Component 与 Client Component 在渲染过程中的主要区别​

维度Server ComponentClient Component
执行环境只在服务器端执行一次在客户端执行(也可能在服务器端做预渲染/SSR,但不是 RSC 核心)
代码包含不会发送到客户端(减小 bundle 体积)打包发给客户端
访问能力可直接访问数据库、文件系统、内部服务只能通过 API 间接访问后端(或通过 props 接收数据)
生命周期/hooks❌ 不能用 useState、useEffect、浏览器 API✅ 可用所有 React hooks 和浏览器 API
异步支持原生支持 async/await,直接在组件内 await fetch需要用 useEffect 或 Suspense + 数据 fetching 库
渲染产物序列化的 React 元素描述(非 HTML)真实的 DOM 节点(或 React 元素)
重新渲染不会因客户端状态变化而重新渲染(除非重新请求整个页面/路由)会因 props/state 变化而正常 re-render
交互性无交互逻辑,只能作为静态内容或数据容器可包含按钮、表单、事件处理等交互

补充说明(结合 SSR 的常见实现)​

在实际框架(如 Next.js App Router)中,为了优化首屏加载体验,往往会额外增加一步 SSR 对 Client Component 的预渲染:

  • 服务器先执行 RSC 流程获得组件树。
  • 然后将树中的 Client Component 单独渲染为静态 HTML 并插入到响应中。
  • 客户端首先显示这些 HTML,再加载 JS 进行水合。

这种混合模式中,上述“Server Component 只生成 Payload”的结论不完全成立(因为最终发送的 HTML 已包含 Client Component 的初始结构)。但从纯粹的 RSC 模型看,核心差异依然如表中所述。如果面试中需要严格区分,建议以标准 RSC 规范为准。