← [ BACK_TO_NOTES ]

Hydration

Hydration 是浏览器复用已有 HTML 并接上客户端组件逻辑、事件与状态的过程;在 RSC 架构中主要发生在需要客户端运行时的边界,而不是所有 Server Component。

#frontend #concept #growing

[!info] related notes

Hydration

一句话定义

Hydration 是前端框架在浏览器中复用已经存在的 HTML,并把客户端组件的逻辑、事件、状态和后续更新能力接上去的过程。

它解决的是:

页面已经“看得见”以后,如何让需要交互的部分真正“活起来”。

为什么它会出现

SSR、SSG 或其他预渲染方案可以提前给浏览器 HTML:

Server / build
   ↓
HTML
   ↓
Browser 立即显示

但 HTML 本身并不知道 React 的:

  • onClick
  • useState
  • useEffect
  • 组件更新关系
  • 客户端路由状态

因此浏览器还需要客户端 JavaScript 把这些能力恢复起来:

已有 HTML
   +
Client Component JS
   ↓
Hydration
   ↓
可交互 UI

Hydration 不是“重新生成一遍页面”

一个常见误解是:服务器生成 HTML 后,React 在浏览器里再完整重画一次。

更准确的理解是:客户端会基于已经存在的 DOM/HTML 建立 React 对应关系,并让后续状态更新与事件系统接管它。

因此 hydration 的核心是 reuse + attach,而不是简单的“第二次渲染”。

首次可见不等于首次可交互

用户可能已经看到:

<button>保存</button>

但相关客户端 JavaScript 尚未加载/执行完成时,这块 UI 的 React 交互能力可能还没有完全就绪。

所以要区分:

  • HTML 已经可见
  • JavaScript 已经可执行
  • 交互区域已经 hydration 完成

详见 首次可见与可交互时间。

在 React Server Components 架构中的变化

传统 SSR 经常需要对较大的 React 应用树做 hydration。

RSC 架构进一步问:

哪些组件实现代码根本不需要进入浏览器?

因此:

  • Server Component 实现留在服务端,不需要作为客户端组件被 hydration。
  • Client Component 边界及需要浏览器 runtime 的部分才需要客户端 JavaScript。

这也是为什么:

Server Component ≠ SSR
Client Component ≠ CSR

一个 Client Component 的初始 UI可以被服务器预渲染,但它仍需要在浏览器中 hydration 才能拥有状态与事件。

与 Islands Architecture 的关系

Islands Architecture 把 hydration 边界进一步显式化:页面大部分保持 HTML,只有标记为交互岛的区域独立 hydration。

Astro 的 client:load、client:idle、client:visible 等指令甚至可以控制岛屿何时加载和 hydration。

它和 RSC 都试图减少不必要的客户端 JavaScript,但实现模型不同。

Hydration Mismatch

如果客户端在 hydration 时“认为应该存在的 UI”和服务器已经输出的 HTML 不一致,就可能出现 hydration mismatch。

常见原因包括:

  • 首屏直接使用 Date.now() / 随机值
  • 服务端和客户端拿到不同数据
  • 渲染阶段直接访问只存在于浏览器的状态
  • 首屏主题、locale 等环境信息两侧不一致

详见 React Hydration Mismatch。

ThemeSwitcher 是一个很典型的边界案例

ThemeSwitcher 本身通常需要:

onClick
localStorage
matchMedia
DOM class / attribute

所以它是 Client Component 很自然。

但“首屏应该是什么主题”是另一个问题。

如果服务端在请求时就能知道主题,例如 Cookie:

Request(cookie=dark)
      ↓
Server render <html class="dark">
      ↓
Browser

这样可以从第一帧就输出正确主题。

方案二:主题只存在 localStorage

服务端读取不到浏览器 localStorage。这时不能说“请求时自动把 localStorage 带给服务器”。常见做法是:

  • 使用尽早执行的小脚本,在页面绘制前读取主题并设置 <html> class/attribute;或
  • 首次客户端运行后再同步主题;或
  • 改为 Cookie,使服务端也能读取主题偏好。

所以:

ThemeSwitcher 是 Client Component,不等于主题的首屏决策一定只能在客户端做;关键取决于主题状态存在哪里。

为什么 Hydration 会成为性能问题

Hydration 不是免费的。客户端仍可能需要:

  • 下载 JavaScript
  • parse / compile
  • 执行模块
  • 建立组件树
  • 绑定事件与状态
  • 占用主线程

所以 SSR 能改善“更早看到内容”,并不自动保证“更早能交互”。

优化方向不是“永远不要 hydration”,而是:

减少需要 hydration 的范围
+
减少 Client JS
+
推迟低优先级交互
+
避免 hydration mismatch

常见误区

CSR 默认也需要 Hydration

不准确。纯 CSR 没有一份由服务器预先生成、等待客户端接管的同一套 UI HTML,因此通常讨论的是客户端首次 render,而不是 hydration。

SSR 完成后页面就完全可交互

不准确。SSR 主要解决 HTML 直出;交互能力仍依赖客户端 runtime。

整个 RSC 页面都要 Hydration

不准确。Server Component 本身不需要作为客户端组件 hydration;重点是 Client Component 边界。

最短记忆方式

SSR / SSG 先把页面画出来,Hydration 把需要客户端交互的部分接活。

学习检查

不看正文回答:

  1. Hydration 的输入为什么是一份已经存在的 HTML,而不是空页面?
  2. SSR / SSG 与 Hydration 分别解决“可见”和“可交互”的哪一部分?
  3. 为什么纯 CSR 通常讨论首次 render,而不是 hydration?
  4. Hydration mismatch 本质上要求哪两侧结果保持什么一致性?
  5. React Server Component 为什么不等于“所有页面都需要 hydration”?
  6. 减少 Hydration 成本可以从哪些方向入手?
  7. 给一个依赖 localStorage 的 ThemeSwitcher,解释为什么容易产生首屏 / hydration 边界问题。
创建于 2026/3/19 更新于 2026/9/30
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本