“脱水”指服务端将VNode转为带数据标记的HTML并附初始状态快照,“注水”指客户端读取快照还原状态、复用DOM并激活响应式系统;二者需严格保持render输入一致以避免hydration mismatch。

“脱水”(Dehydration)和“注水”(Hydration)不是 Vue 或 React 的专有术语,而是 SSR 场景下描述服务端与客户端之间 VNode 状态流转的通用隐喻。它们指的是一组关键动作:服务端把渲染结果连同数据“挤干水分”打包发给浏览器;客户端拿到后,再把逻辑、事件、响应式系统“加水复活”,让静态 HTML 变成可交互的 Vue 应用。
脱水:服务端生成可序列化的 VNode 快照
脱水发生在 Node.js 环境中,本质是将 Vue 实例在服务端完成首次渲染后生成的 VNode 树,转换为带数据标记的 HTML 字符串,并附带一份“初始状态快照”。这个快照通常以 script type="application/json" 标签内联在页面底部,例如:
<script id="__INITIAL_STATE__" type="application/json">{"user":{"id":123,"name":"张三"}}</script>
同时,vue-server-renderer 会为每个组件生成唯一的 serverPrefetch 数据标识,并在 HTML 中插入 data-server-rendered="true" 属性,告诉客户端:“这部分是我渲染的,别重复画”。脱水不传输完整的 VNode 对象(因为不可序列化),而是保留其结构语义——比如哪些节点含响应式插值、哪些绑定了事件占位符、哪些需要等 hydration 后才激活。
注水:客户端用状态快照“唤醒”静态 DOM
注水是浏览器中 new Vue({ ... }).$mount() 的核心环节。Vue 会做三件事:
生成Blender Python (bpy)脚本,该脚本使用node_groups API和GeometryNodeTree接口程序化地创建几何节点修改器树,用于参数化3D资产生成。
- 读取 __INITIAL_STATE__,还原 store 或 data 初始值,避免二次请求
- 遍历 DOM,比对 data-server-rendered 和组件 render 函数产出的 VNode 结构,跳过完全一致的节点(即“跳过重绘”)
- 为绑定事件的元素(如
@click)、v-model 输入框、watcher 依赖等挂载响应式系统,使 DOM “活过来”
这个过程不是重新渲染整页,而是复用已有 DOM 节点,仅修补差异(类似 diff),所以速度快、无闪烁。若服务端 VNode 与客户端 render 出的 VNode 不匹配(比如 props 类型错、条件渲染分支不同),Vue 会警告并强制丢弃服务端 HTML,退化为 CSR 渲染——这就是常说的“hydration mismatch”。
VNode 同步的关键实战细节
VNode 同步不是自动可靠的,需开发者主动保障一致性:
-
避免客户端独有逻辑污染服务端:像
window、document、localStorage直接调用必须包裹在if (typeof window !== 'undefined')或生命周期钩子(如mounted)中 -
服务端预取数据必须可序列化:
serverPrefetch返回的对象不能含函数、Date、RegExp、Map/Set 等无法 JSON.stringify 的值 - 组件 key 和 slot 内容需稳定:动态 key 或服务端/客户端 slot 分支不一致,极易导致 hydration 失败
- 样式同构要对齐:CSS-in-JS 库(如 styled-components)需启用 SSR 支持,否则服务端生成的 class 名与客户端不一致,VNode 结构比对失败
真正实现“无缝同步”,靠的不是魔法,而是两端 render 函数输入完全一致:相同的 props、相同的 data 初始值、相同的环境判断、相同的副作用时机。










