导读:本期聚焦于又改需求创作的《React Server Components 如何实现服务端与客户端组件混合开发?》,敬请观看详情。React Server Components 引入了一种全新的组件渲染模型,它允许开发者在服务器上直接执行组件逻辑,只把轻量化的结果发送到浏览器。这种机制和传统的 SSR 不同,服务端组件不会被打包进客户端 JavaScript,因此可以显著减小 bundle 体积,同时安全地访问数据库、文件系统等后端资源。混合开发的核心在于通过 use client 指令标记客户端组件,从而在同一个组件树中组合服务端与客户端逻辑。服务端组件负责数据获取和静态渲染,客户端组件处理交互和状态,两者通过可序列化的 props 进行通信。理解这种边界划分以及序列化限制,是高效使用 RSC 的关键。本文会从运行机制、边界划分、实践案例和常见限制几个方面展开分析,帮助读者掌握 React 服务端组件与客户端组件的混合开发模式。

React Server Components(RSC)是 React 团队推出的一种新组件类型,它重新定义了组件在何处运行以及如何与客户端协作。与传统的客户端渲染或者服务端渲染(SSR)不同,RSC 允许部分组件只在服务器上执行,而另一部分组件在浏览器中运行,两者可以无缝组合在同一棵组件树中。这种混合模型并不是要取代现有的客户端组件,而是提供了一种更细粒度的能力划分方式,让开发者能够根据组件的职责选择运行环境。

React Server Components 如何实现服务端与客户端组件混合开发?

理解 RSC 的关键在于区分组件代码的“执行位置”和“渲染输出”。服务端组件在服务器上运行,可以直接访问后端资源,比如通过 ORM 查询数据库、读取文件系统或者调用内部 API。它们不会被包含在发送给浏览器的 JavaScript bundle 中,因此可以大幅减少前端需要下载的代码量。客户端组件则和传统的 React 组件一样在浏览器中执行,负责处理用户交互、管理本地状态以及使用浏览器专属 API。通过一种称为“序列化树”的传输格式,服务端组件渲染出的结果会被流式发送到客户端,其中穿插着客户端组件的占位符。

服务端组件与客户端组件的边界划分

在 React Server Components 模型中,组件默认是服务端组件。如果你需要让某个组件在客户端运行,就必须在该文件顶部添加 'use client' 指令。这个指令告诉打包工具该模块及其依赖的模块都需要被打包到客户端 bundle 中。需要注意的是,'use client' 只是一个边界标记,它并不改变组件本身的语法,你仍然可以像编写普通 React 组件一样使用 hooks 和事件处理器。

服务端组件和客户端组件之间有一条严格的序列化边界。服务端组件可以将数据作为 props 传递给客户端组件,但这些 props 必须是可序列化的,比如字符串、数字、布尔值、数组、普通对象以及 React 元素。函数、类实例、Symbol、Promise 等不可序列化的内容不允许直接通过 props 传递。这意味着你不能把一个事件处理函数从服务端组件传给客户端组件,也不能把从数据库查询出的 ORM 实体对象直接传过去,必须先将它们转换为普通 JavaScript 对象。

这种限制看似不便,实际上推动了更清爽的组件设计。数据获取逻辑被集中在服务端组件中,客户端组件只接收已经整理好的数据并专注于交互。举例来说,一个列表页面可以由服务端组件负责从数据库读取文章列表,然后把每篇文章的标题和摘要作为 props 传给一个客户端组件,该客户端组件内部使用 useState 管理展开和收起的状态。这样数据库查询逻辑完全不会出现在客户端代码中,也避免了在浏览器中暴露数据库连接信息。

混合开发模式的核心运行机制

React Server Components 的渲染流程从服务器上的根组件开始。框架(如 Next.js)会执行服务端组件的渲染函数,生成一个称为 RSC Payload 的序列化数据结构。这个 payload 中包含了服务端组件渲染出的文本节点、样式信息以及客户端组件的引用位置。浏览器接收到 payload 后,React 会重建组件树,对于客户端组件的引用,会加载对应的 JavaScript 模块并在客户端执行其渲染逻辑。整个过程中,服务端组件的代码不会出现在浏览器中,只有它们渲染出的结果被发送过去。

这种机制带来的一个显著优势是减少冗余代码。假设一个组件只做静态展示,不依赖任何客户端状态,那么它完全可以作为服务端组件存在,其依赖的库(比如日期格式化工具或 Markdown 解析器)也不会进入客户端 bundle。而如果同一个组件被标记为客户端组件,那么这些依赖都必须被浏览器下载。通过混合使用,开发者可以精确控制哪些代码被送到客户端,从而优化加载性能。

另一个关键点是数据获取的方式。在传统 React 应用中,数据通常在客户端通过 useEffect 发起网络请求获得,这会导致额外的往返延迟和闪屏。而在 RSC 中,服务端组件可以直接使用 async/await 获取数据,渲染完成后再把最终 HTML 片段和序列化数据返回给客户端。这样既减少了客户端的请求次数,也避免了多层数据获取造成的瀑布流问题。不过要注意,服务端组件不能使用 useStateuseEffect 等客户端 hooks,因为它们只在服务器上运行一次,不存在交互状态。

实践:构建一个混合组件树

下面通过一个具体例子展示如何组合服务端组件和客户端组件。假设我们有一个用户信息卡片,希望由服务端组件从数据库读取用户数据,而卡片上的点赞按钮需要在客户端处理点击事件。首先编写服务端组件,它负责获取数据并渲染客户端组件。

// UserProfile.js(服务端组件)
import { db } from './database';
import LikeButton from './LikeButton';

export default async function UserProfile({ userId }) {
  const user = await db.user.findUnique({ where: { id: userId } });
  const plainUser = {
    name: user.name,
    avatarUrl: user.avatarUrl,
    bio: user.bio,
  };

  return (
    <section>
      <h2>{plainUser.name}</h2>
      <img src={plainUser.avatarUrl} alt={plainUser.name} />
      <p>{plainUser.bio}</p>
      <LikeButton initialCount={user.likes} />
    </section>
  );
}

在上面的代码中,UserProfile 是一个服务端组件,它使用 async/await 查询数据库并返回 JSX。注意 <LikeButton /> 是一个客户端组件,它接收 initialCount 作为初始点赞数。由于 user.likes 是一个数字,它满足可序列化条件,因此可以安全传递。接下来看看客户端组件的实现。

// LikeButton.js(客户端组件)
'use client';

import { useState } from 'react';

export default function LikeButton({ initialCount }) {
  const [count, setCount] = useState(initialCount);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <button onClick={handleClick}>
      点赞 {count}
    </button>
  );
}

这里 'use client' 指令告诉 React 和打包器这个文件需要被当作客户端组件处理。组件内部使用 useState 管理点赞数量,这是完全合法的,因为它会在浏览器中重新挂载和更新。用户点击按钮时,只有 LikeButton 组件重新渲染,服务端组件返回的静态内容不会受到影响。这种模式清晰地将数据获取和交互逻辑分离开来。

在实际项目中,可能需要在一个客户端组件内部再使用服务端组件吗?答案是否定的,因为一旦模块被标记为 'use client',其内部导入的所有组件都会被当作客户端组件处理,无法再恢复为服务端组件。因此,最合理的做法是将客户端组件放在组件树的叶子节点或者靠近叶子的位置,让服务端组件负责数据流的主干。如果你发现需要从客户端组件向服务端组件传递数据,正确的做法是将服务端组件作为 children 通过 props 传入,框架会自动处理序列化边界。

常见限制与最佳实践

使用 React Server Components 时最常遇到的限制就是 props 序列化。除了函数和类实例之外,像 Date 对象、MapSet 等也需要先转换为普通对象或数组。另一个容易遗漏的点是环境变量的使用:客户端组件中无法访问服务器端的环境变量,必须通过显式的 props 传递。如果你的客户端组件依赖了某个只在 Node.js 中可用的包,就需要考虑将其拆分为服务端组件,或者使用动态导入在客户端按需加载。

性能方面,RSC 并不是万能的。对于高度交互的密集型应用,比如实时协作编辑器或者复杂的表单,过度使用服务端组件可能会导致大量序列化数据传输和反复重建。此时应该把交互密集的部分放在客户端组件中,服务端组件只负责外壳和初始数据。此外,服务端组件不应该包含任何定时器、订阅或浏览器事件监听,因为这些逻辑在服务器上毫无意义。遵循“服务端组件获取数据,客户端组件处理交互”的原则有助于保持代码清晰。

在实际开发中,可以使用 Next.js 的 App Router 来体验 RSC。创建一个 page.js 作为服务端组件,然后在其中导入带有 'use client' 指令的组件。Next.js 会自动处理 RSC 的构建和序列化,你只需关注组件本身的职责划分。随着 React 19 的稳定,Server Components 已经成为正式特性,理解其混合开发模式对于构建现代 React 应用至关重要。建议从小型模块开始实践,逐步将数据获取逻辑迁移到服务端组件,并观察 bundle 体积和首屏加载时间的变化。

React Server Components服务端组件客户端组件修改时间:2026-08-20 16:51:07

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。