← 返回题目列表

什么是 Hydration?为什么会出现水合不一致?

高频 困难 第 13 / 25 题 更新于 2026/07/28
SSRHydration同构水合

简化版

Hydration 是客户端接管服务端已渲染 HTML 的过程:浏览器先展示服务端 HTML,然后下载 JS,框架绑定事件并恢复组件状态。水合不一致通常是因为服务端和客户端首次渲染结果不同,比如使用随机数、时间、浏览器 API、环境差异或异步数据不一致。

详细版

SSR 页面初始 HTML 来自服务端,但交互能力需要客户端 JS 接管。

Hydration 做的事:

  • 复用已有 DOM。
  • 建立组件实例。
  • 绑定事件监听。
  • 恢复状态。
  • 检查客户端渲染结果是否和服务端 HTML 匹配。

水合不一致原因:

  • Date.now()Math.random()
  • 服务端没有 window/document。
  • 客户端和服务端数据不同。
  • 用户登录态只在客户端可知。
  • 条件渲染依赖设备宽度。
  • HTML 结构不合法被浏览器自动修正。

解决方式是保证首屏渲染确定性,把客户端专属逻辑延后到 mounted/effect。

完整版教学

一、为什么需要 Hydration

SSR 返回的 HTML 能让用户快速看到页面,但 HTML 本身没有完整前端应用状态。按钮点击、组件状态、路由跳转等能力需要客户端 JS 接管。

Hydration 就是把静态 HTML 变成可交互应用的过程。

它不是重新生成一遍 DOM,而是尽量复用服务端已经生成的 DOM,然后绑定事件和状态。

二、水合不一致是什么

框架会在客户端重新计算首屏应该长什么样。如果计算结果和服务端 HTML 不一致,就会出现 hydration mismatch。

轻则控制台警告,重则客户端丢弃服务端 DOM 重新渲染,导致闪烁、性能下降甚至页面错误。

三、常见原因和处理

随机数、当前时间、浏览器宽度、localStorage、用户时区等都会导致服务端和客户端结果不同。

处理原则:

  • 首屏渲染使用确定数据。
  • 浏览器 API 放到客户端生命周期中。
  • 依赖用户环境的内容先占位,客户端接管后再渲染。
  • 服务端和客户端共用同一份初始数据。

四、面试追问与工程落地

面试官可能问:“为什么服务端不能直接读取 localStorage?”

localStorage 是浏览器 API,服务端没有浏览器环境。服务端只能读取请求中的 Cookie、Header、URL 等信息。

工程中 SSR 代码要区分通用逻辑和客户端专属逻辑,避免在服务端渲染阶段访问 window、document、navigator。

五、从 HTML 到可交互页面的时间线

0ms 收到 HTML → 80ms 首屏可见 → 300ms JS 下载完成
→ 360ms 组件树重建/核对 → 420ms 事件可用

上例中内容在 80ms 可见,但按钮到 420ms 才真正可交互;这段间隔常被称为 hydration gap。若 JS 很大或主线程繁忙,SSR 的“看得见”不等于“点得动”。

阶段浏览器/框架工作可能瓶颈
HTML 解析建 DOM、发现资源文档体积、阻塞资源
JS 加载下载与解析 bundle体积、网络、缓存
首次客户端渲染用相同输入计算树不确定数据
水合提交复用 DOM、绑定事件CPU、组件数量

水合的契约是“服务端输出与客户端第一次计算一致”;客户端第二次 effect 更新可以不同,但第一次不能偷偷依赖浏览器环境。

六、定位 mismatch 的方法和边界

先保存服务器原始 HTML,再在禁用扩展的干净浏览器中捕获水合前 DOM,比较文本、属性和节点层级。浏览器会自动修正非法 HTML,例如把不允许嵌套的元素移位,所以源字符串看似一致,实际 DOM 也可能不同。

Date.now()、随机 id、时区格式化、A/B 分组、媒体查询和 localStorage 逐项替换成服务器注入的确定值。比如服务器生成随机 id a12,客户端必须复用 a12,不能再调用一次 random 得到 b91

suppressHydrationWarning 只适合明确、局部、不可避免的文本差异,不是修复手段;滥用会隐藏真实结构错误。客户端-only 可用于地图、编辑器等确实无法 SSR 的组件,但会失去该模块的首屏 HTML 与 SEO 收益。

// 首屏先使用服务器传入值,挂载后再读取浏览器偏好
const initialTheme = serverTheme
// effect/mounted 中读取 localStorage 并更新

七、常见误区与追问

  • 误区:水合不一致只是无害的控制台警告。 框架可能重建 DOM、丢失状态或产生事件和视觉异常。
  • 误区:用 typeof window 条件渲染就一定正确。 服务端 false、客户端首次 true 仍会生成不同树。
  • 误区:所有问题都用 client-only 包裹最省事。 会放弃 SSR 内容并推迟模块可见和可交互时间。
  • 追问:为什么非法 HTML 会导致 mismatch? 浏览器解析器会自动修正节点结构,与框架预期虚拟树不同。
  • 追问:时间和时区怎样稳定? 服务端传入固定时间值和明确时区,客户端首次渲染复用,挂载后再本地化。
  • 追问:水合为什么消耗 CPU? 客户端仍要加载代码、重建组件状态并核对/绑定已有 DOM。
  • 追问:如何缩短 hydration gap? 减少客户端 JS、拆分交互边界、延迟非关键脚本并避免主线程长任务。

八、加强记忆

  1. 定义:水合让已有 SSR DOM 获得组件状态和事件能力。
  2. 核心合同:同样初始输入必须产生同样首屏树。
  3. 高危来源:时间、随机数、时区、浏览器 API、非法 HTML 和不同数据。
  4. 定位方法:比较原始 HTML、水合前 DOM 与客户端首次计算。
  5. 正确修复:注入确定值,浏览器专属变化放挂载后。
  6. 性能目标:不仅看内容可见,还要测 JS 成本和交互可用时间。