关于 react-server-components
介绍 React Server Components 及其在服务端驱动渲染中的作用,适合希望减少客户端 JavaScript 的 React 开发者,并阐明其与 SSR、交互式客户端组件的区别。
主要功能
- 01在服务端获取数据并渲染
- 02不向客户端发送服务端组件代码
- 03用客户端组件实现交互
- 04支持自动代码拆分
- 05重新获取服务端片段时保留客户端状态
适用场景
- 01以较少客户端 JavaScript 渲染非交互界面
- 02将依赖浏览器的交互留给客户端组件
- 03刷新服务端结果而不丢失输入状态
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ patternsdev/ skills --skill react-server-components安装后在 Claude Code 中输入/react-server-components确认已加载。
详细介绍
React Server Components
目录
React 的 Server Components 支持采用服务器驱动的思维模式,打造现代化用户体验。这与组件的服务器端渲染(SSR)截然不同,并能显著缩小客户端 JavaScript 包。
适用场景
- 想要通过在服务器上运行数据获取和渲染来减少客户端 JavaScript 时,使用此方案。
- 这有助于借助 Next.js 13+ App Router 中零 JavaScript 成本的服务器渲染组件提升性能。
不适用场景
- 组件需要客户端交互时——状态(
useState)、副作用(useEffect)和事件处理程序都需要 Client Components。 - 组件依赖仅限浏览器使用的 API(例如
window、localStorage、IntersectionObserver)时。 - 组件本身已经很小,而服务器与客户端的边界带来的复杂性超过了它所节省的成本时。
说明
- 使用 Server Components(在 Next.js App Router 等框架中默认启用)获取数据并构建非交互式 UI。
- 仅对需要交互的组件(事件处理程序、状态、副作用)添加
'use client'指令。 - Server Components 可以使用重量级库(Markdown 解析器、日期格式化工具),而不会增加客户端包的开销。
- Server Components 与 SSR 相辅相成,并非替代 SSR。
- 如果框架支持,使用 Server Functions 或 Server Actions(
'use server')处理表单提交和数据变更。
详情
更新(React 19 / 现代框架): 对于框架用户来说,React Server Components 现已成为正式版功能。与传统 SSR 不同,RSC 允许你在服务器上渲染部分 UI,而无需将该组件的 JavaScript 发送到客户端。容器/展示组件模式非常适合这种场景:“容器”可以是一个 Server Component,用于获取数据并将其传递给可交互的 Client Component。
在 Next.js App Router 中,你不再使用
getServerSideProps——取而代之的是,app/目录中的任何 React 组件都可以是异步组件,以便在服务器上获取数据。React Server Components 并非 SSR 的替代方案,而是与之相辅相成。通常,你会将 RSC 用于页面的大部分内容(作为 SSR 的一部分进行渲染和流式传输),并为需要交互的组件添加'use client'指令。
'use server'指令用于 Server Functions 或 Server Actions,而不是用来将组件标记为 Server Component。Server Components 无需指令;在支持它们的框架中,它们是默认选项,除非你选择使用'use client'。
React Server Components 现已在 Next.js App Router 等框架中达到生产就绪状态。以下资源最适合作为入门指南:
- React Server Components 参考文档是目前最好的概览。
- RFC仍有助于深入了解实现背景。
- Next.js App Router 文档介绍了 Server Components 的现代用法。
- Shopify Hydrogen 和 Server Components
服务端渲染的局限性
如今,客户端 JavaScript 的服务端渲染效果可能并不理想。组件的 JavaScript 会在服务器上渲染为 HTML 字符串。浏览器收到这段 HTML 后,可能会因此更快地显示首次内容绘制或最大内容绘制。
不过,为了实现交互性,仍然需要获取 JavaScript,这通常通过水合步骤实现。服务端渲染通常用于初次加载页面,因此水合完成后,基本不会再次用到它。
注意: 虽然确实可以构建一个仅在服务器端运行的 React 应用,利用 SSR 并完全避免在客户端进行水合,但模型中的复杂交互通常需要跳出 React。Server Components 支持的混合模式,可以让你针对每个组件分别做出选择。
借助 React Server Components,我们可以定期重新获取组件。包含在有新数据时会重新渲染的组件的应用可以在服务器上运行,从而减少需要发送到客户端的代码量。
在 Server Components 出现之前,使用 Markdown 渲染器和净化器会增加打包体积:
// *Before* Server Components
import marked from "marked"; // 35.9K (11.2K gzipped)
import sanitizeHtml from "sanitize-html"; // 206K (63.3K gzipped)
function NoteWithMarkdown({text}) {
const html = sanitizeHtml(marked(text));
return (/* render */);
}
Server Components
React 的新功能 Server Components 是对服务端渲染的补充,它们可以将内容渲染为中间抽象格式,而无需增加 JavaScript 包的体积。这样既可以在不丢失状态的情况下合并服务器树和客户端树,也能支持扩展到更多组件。
Server Components 并不能取代 SSR。二者配合使用时,可以先快速渲染为中间格式,再由服务端渲染基础设施将其渲染为 HTML,从而确保首次绘制仍然足够快。我们会对 Server Components 输出的客户端组件进行 SSR,类似于 SSR 与其他数据获取机制的搭配方式。
不过,这样生成的 JavaScript 包会显著变小。早期探索显示,打包体积有望大幅缩小(-18-29%),但 React 团队要等后续基础设施工作完成后,才能更准确地了解实际效果。
import marked from "marked"; // zero bundle size
import sanitizeHtml from "sanitize-html"; // zero bundle size
function NoteWithMarkdown({text}) {
// same as before
}
自动代码拆分
最佳实践一直是通过代码拆分,只在用户需要时才提供所需代码。这样可以将应用拆分成更小的包,减少发送到客户端的代码量。在 Server Components 出现之前,人们通常会手动使用 React.lazy() 定义“拆分点”,或依赖元框架(例如路由/页面)设置的启发式规则来创建新的代码块。
在 Server Components 出现之前:
// *Before* Server Components
import React from "react";
// one of these will start loading *when rendered on the client*:
const OldPhotoRenderer = React.lazy(() => import("./OldPhotoRenderer.js"));
const NewPhotoRenderer = React.lazy(() => import("./NewPhotoRenderer.js"));
function Photo(props) {
// Switch on feature flags, logged in/out, type of content, etc:
if (FeatureFlags.useNewPhotoRenderer) {
return <NewPhotoRenderer {...props} />;
} else {
return <PhotoRenderer {...props} />;
}
}
代码拆分的一些挑战:
- 如果不使用元框架(如 Next.js),通常必须手动处理这项优化,将
import语句替换为动态导入。 - 这可能会延迟应用开始加载组件的时间,影响用户体验。
Server Components 引入了自动代码拆分机制,将客户端组件中的所有常规导入都视为潜在的代码拆分点。它们还允许开发者更早地(在服务器上)选择要使用的组件,使客户端能够在渲染过程的更早阶段获取该组件。
使用 Server Components 时:
import React from "react";
// one of these will start loading *once rendered and streamed to the client*:
import OldPhotoRenderer from "./OldPhotoRenderer.client.js";
import NewPhotoRenderer from "./NewPhotoRenderer.client.js";
function Photo(props) {
// Switch on feature flags, logged in/out, type of content, etc:
if (FeatureFlags.useNewPhotoRenderer) {
return <NewPhotoRenderer {...props} />;
} else {
return <PhotoRenderer {...props} />;
}
}
Server Components 会取代 Next.js SSR 吗?
不会。它们截然不同。随着研究和实验的继续,Server Components 的早期应用实际上会通过 Next.js 等元框架进行探索。
Dan Abramov 总结了 Next.js SSR 和 Server Components 之间的区别:
- Server Components 的代码永远不会发送到客户端。 在许多使用 React 实现 SSR 的方案中,组件代码仍会通过 JavaScript 包发送到客户端。这可能会延迟交互。
- Server Components 支持在组件树的任意位置访问后端。 在较早的 Next.js 数据 API 中,逻辑通常必须放在页面级函数中,例如
getServerSideProps()。使用 Server Components 时,数据获取逻辑可以放在更靠近所需组件的位置。 - 重新获取 Server Components 时,可以保留组件树内部的客户端状态。 这是因为主要传输机制比单纯的 HTML 丰富得多,因此可以重新获取服务器渲染的部分内容(例如搜索结果列表),而不丢失其内部状态(例如搜索框中的文本、焦点和文本选区)。
Server Components 的部分早期集成工作将通过 webpack 插件完成,该插件会:
- 定位所有客户端组件
- 创建 ID => 分块 URL 的映射
- Node.js 加载器会将对客户端组件的导入替换为对此映射的引用。
- 其中一些工作需要更深入的集成(例如与路由等部分的集成),因此让它能够与 Next.js 这样的框架协同工作会很有价值。
正如 Dan 所指出的,这项工作的目标之一是让元框架变得更好。