注水阶段需精准复用服务端DOM,确保结构一致、时机可控、副作用隔离;禁用浏览器API与非确定性逻辑;CSS-in-JS须SSR支持;事件绑定延迟至hydration完成;跳过静态区域;动态组件须服务端客户端一致;业务逻辑需等待hydration真正结束。

注水阶段(Hydration)不是简单“接上”服务端 HTML,而是让客户端 Vue/React 实例精准复用已存在的 DOM 节点,并在正确时机绑定事件、激活响应式系统。DOM 与事件解绑无缝对齐的关键,在于结构一致、时机可控、副作用隔离。
确保服务端与客户端渲染输出完全一致
这是对齐的前提。任何结构差异都会导致框架放弃复用、触发重挂载,事件自然无法延续。
- 避免在 render 或 setup 中直接读取 window、document、navigator 等浏览器专属对象;服务端无此环境,返回
undefined或报错,造成 vnode 不匹配 - 禁用非确定性逻辑:如
Date.now()、Math.random()、依赖视口尺寸的判断(innerWidth)、用户代理检测等;应统一由服务端注入稳定值(如请求时间戳、设备类型标记) - CSS-in-JS 必须启用 SSR 支持,服务端调用
collectStyles或extractCritical,将关键样式内联到 HTML;否则客户端补样式会引发重排,间接破坏 DOM 结构一致性
事件绑定必须延迟到 hydration 完成后
服务端生成的 HTML 是静态的,不包含任何事件监听器。客户端不能在挂载前就尝试绑定,也不能在 hydration 过程中重复绑定。
- Vue 中,
onMounted、onActivated是安全边界 —— 它们只在客户端执行,且保证 DOM 已被 Vue 接管 - React 中,
useEffect(空依赖数组)是等效时机;useLayoutEffect可用于需同步读取 DOM 尺寸的场景,但不可发起数据请求或修改状态 - 禁止在
setup函数体或组件顶层直接调用addEventListener;这类代码会在服务端执行,导致错误或无效绑定
主动隔离不可水合区域,减少干扰面
并非所有 DOM 都需要事件接管。强行对静态内容水合,反而增加 JS 执行负担,拖慢 TTI,还可能因结构微小差异引发警告。
- 对纯展示区块(如页脚版权、静态 banner),使用
v-html(Vue)或dangerouslySetInnerHTML(React),配合服务端注释标记(如)或suppressHydrationWarning属性跳过校验 - 采用渐进式水合(Progressive Hydration):优先激活首屏可视区内的交互组件(如搜索框、商品卡片),其余区域延迟或按需激活
- 动态组件(
)需确保服务端和客户端 resolve 的组件名/路径完全一致;异步组件建议配合defineAsyncComponent+loading/errorfallback,避免服务端 fallback 而客户端不 fallback 导致结构错位
验证 hydration 是否真正完成再触发交互逻辑
有些业务逻辑(如自动聚焦输入框、滚动定位、第三方 SDK 初始化)必须等 hydration 完全结束、事件系统就绪后再运行。
- Vue 3.4+ 提供
onHydrated钩子,明确标识 hydration 流程结束;比onMounted更精确 - React 中可监听
hydrateRoot返回的callback,或使用useEffect+document.readyState === 'complete'做双重保险 - 避免在路由守卫(如
beforeEach)中立即操作 DOM;应等待当前组件 hydration 完成,可通过nextTick(Vue)或setTimeout(..., 0)(React)微任务延后











