关于 react-selective-hydration
面向 React 18 SSR 开发者的选择性水合指南,说明如何通过流式渲染和 Suspense,让已就绪的页面部分先变为可交互,而不必等待所有组件。
主要功能
- 01使用 Suspense 划分水合区域
- 02通过流式 SSR 提前发送 HTML
- 03慢速组件不阻塞同级内容
- 04组件到达客户端后即可水合
适用场景
- 01让 SSR 页面部分内容先可交互
- 02避免数据获取组件阻塞页面水合
- 03在大型组件加载期间保持关键区域可用
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ patternsdev/ skills --skill react-selective-hydration安装后在 Claude Code 中输入/react-selective-hydration确认已加载。
详细介绍
选择性水合
在之前的文章中,我们介绍了带水合的 SSR 如何改善用户体验。React 可以使用 react-dom/server 库提供的 renderToString 方法,在服务器上快速生成一棵树,并在整棵树生成后将其发送到客户端。渲染后的 HTML 无法交互,直到 JavaScript 包被获取并加载完毕,之后 React 才会遍历这棵树进行水合并附加事件处理器。
然而,由于当前实现存在一些限制,这种方式可能会导致一些性能问题。
适用场景
- 如果你希望 SSR 页面中的部分内容在所有 JavaScript 加载完成前即可交互,请使用此方式
- 如果加载缓慢的组件(例如,获取数据的组件)阻塞了整个页面的水合,此方式会很有帮助
说明
- 使用
Suspense边界划分可独立水合的 UI 区块 - 使用
renderToPipeableStream(Node)或renderToReadableStream(edge)实现流式 SSR - 将耗时较长的数据获取组件放在
Suspense内部,避免其延迟同级组件的水合 - 确保关键的交互组件不处于持续时间较长的加载回退 UI 中
- 使用
hydrateRoot(React 18+)以利用选择性水合
详情
在服务端渲染的 HTML 树能够发送到客户端之前,所有组件都必须准备就绪。这意味着,依赖外部 API 调用或任何可能造成延迟的流程的组件,可能会阻碍较小的组件快速完成渲染。
除了树生成速度较慢之外,另一个问题是 React 只会对树进行一次水合。这意味着,在 React 能够对任何组件进行水合之前,它需要先获取所有组件的 JavaScript。这意味着,较小的组件(代码包更小)必须等待较大组件的代码获取并加载完毕,React 才能对网站上的任何内容进行水合。在此期间,网站一直无法交互。
React 18 通过允许我们将流式服务端渲染与一种新的水合方式相结合,解决了这些问题:选择性水合!
现代 React SSR 不再使用 renderToString,而是在 Node 运行时中使用 renderToPipeableStream(),或在 Web Stream 运行时中使用 renderToReadableStream()。
这些 API 与 hydrateRoot() 和 Suspense 结合后,即可在无需等待较大组件准备就绪的情况下开始流式传输 HTML。这意味着,在使用 SSR 时,我们可以延迟加载组件,而不会阻碍页面其余部分的水合。
之前拖慢树生成速度和 TTI 的 Comments 组件,现在被包裹在 Suspense 中。这会告诉 React,不要让此组件拖慢其余树的生成速度。相反,React 会将回退组件作为初始渲染的 HTML 插入,并继续生成其余的树,然后再将其发送到客户端。
与此同时,我们仍在获取 Comments 组件所需的外部数据。
选择性水合使我们能够在 Comments 组件发送到客户端之前,就对已经发送到客户端的组件进行水合!
Comments 组件的数据准备就绪后,React 会开始流式传输此组件的 HTML,以及一个用于替换回退加载器的小型 <script>。
注入新的 HTML 后,React 会开始水合。
React 18 修复了人们使用 React SSR 时经常遇到的一些问题。
流式渲染允许你在组件准备就绪后立即开始流式传输,而不会因为某些组件在服务端生成耗时较长而导致 FCP 和 TTI 变慢。
组件一旦流式传输到客户端,就可以立即进行水合,因为我们不再需要等待所有 JavaScript 加载完毕才能开始水合;即使尚未对所有组件完成水合,也可以开始与应用交互。