虚拟 DOM 是用轻量 JavaScript 对象树描述真实 DOM 的结构与状态,通过 render→diff→patch 三步实现最小化更新,降低重排重绘开销。

JavaScript 实现虚拟 DOM 的核心,是用轻量的 JavaScript 对象树来描述真实 DOM 的结构和状态,再通过高效的 diff 算法比对新旧虚拟树差异,最后只更新真实 DOM 中真正变化的部分。它不直接提升单次渲染速度,而是显著减少不必要的 DOM 操作——而 DOM 操作恰恰是前端性能瓶颈所在。
虚拟 DOM 是什么?不是真实节点,而是“描述”
虚拟 DOM(Virtual DOM)本质上是一组嵌套的普通 JS 对象,每个对象代表一个 DOM 元素,包含 tag(标签名)、props(属性/事件)、children(子节点数组)等字段。例如:
{
tag: 'div',
props: { className: 'container', onClick: handleClick },
children: [
{ tag: 'span', props: {}, children: ['Hello'] }
]
}
它不持有任何浏览器 API,也不触发重排重绘,创建和遍历成本极低。
关键三步:render → diff → patch
每次状态变化时,框架执行以下流程:
立即学习“Java免费学习笔记(深入)”;
- render:调用组件函数,生成一棵新的虚拟 DOM 树(纯计算,无副作用)
- diff:将新树与上一次的旧虚拟树进行对比,找出最小变更集(如节点移动、属性修改、文本替换),跳过整棵树递归(靠 key 和层级限制优化)
-
patch:把 diff 结果转化为真实 DOM 操作(
appendChild、setAttribute、textContent =等),批量、精准、最小化执行
为什么比直接操作 DOM 快?
真实 DOM 操作慢,不只是因为 JS 调用开销,更因每次修改都可能触发:
- 浏览器样式计算(Style Recalculation)
- 布局(Layout / Reflow)——重新计算所有元素几何信息
- 绘制(Paint)和合成(Composite)
虚拟 DOM 把这些昂贵操作“收口”到 patch 阶段,并通过以下方式压缩代价:
- 批量更新:多个状态变化合并为一次 patch(React 的 batched updates)
- 跳过静态子树:如 React.memo、Vue 的 v-once 或静态提升(hoistStatic)
- 避免强制同步读取:不鼓励在修改前读 offsetHeight 等,防止 layout thrashing
注意:虚拟 DOM 不是银弹
它适合中高频更新、结构较复杂的 UI 场景(如后台系统、富交互表单)。但对以下情况未必最优:
- 极简静态页面(手写 innerHTML 更快)
- 超高性能要求场景(如 Canvas 渲染图表、游戏帧动画)
- 大量列表滚动(需配合 windowing,如 react-window,否则仍会创建过多虚拟节点)
现代框架也在弱化虚拟 DOM:SolidJS 用编译时响应式替代运行时 diff;Svelte 将模板直接编译为高效 DOM 操作代码——说明虚拟 DOM 是权衡之选,而非终极方案。











