人人都会AI编程

5.2 虚拟 DOM 的核心价值:跨平台抽象、批量更新、降低 DOM 操作开销

更新时间:2026-07-11

很多人第一次听到“虚拟 DOM”时,会觉得它是个“多此一举”的东西:既然最终还是要操作真实 DOM,为什么要先在 JavaScript 对象上折腾一遍?但实际上,虚拟 DOM 解决的恰恰是“怎么高效、灵活地操作真实 DOM”的问题。它的核心价值可以概括为三点。

跨平台抽象:一次描述,多端渲染

虚拟 DOM 本质上是一个用 JavaScript 对象描述的界面树,它不依赖浏览器环境。比如一个简单的 <div>

// 虚拟 DOM 节点(简化结构)
const vnode = {
  tag: 'div',
  props: { id: 'app' },
  children: [
    { tag: 'span', props: null, children: 'Hello' }
  ]
}

这个对象与平台无关。同样的虚拟 DOM 树,在不同平台可以用不同的渲染器来转换成最终的视图:

  • 在浏览器中,渲染器将虚拟 DOM 映射为真实 DOM 操作。
  • 在 Weex 或 UniApp 中,渲染器将其映射为原生移动端组件。
  • 在服务端渲染中,渲染器将其序列化为 HTML 字符串。

这意味着 Vue 的核心运行时和响应式系统是平台无关的,只需要替换渲染模块,就能让同一套组件代码运行在不同的终端上。Vue 3 的 createRenderer API 甚至允许开发者自定义渲染器,比如将虚拟 DOM 渲染到 Canvas、WebGL 或者 iOS/Android 原生视图。这种跨平台能力是直接操作真实 DOM 无法做到的。

批量更新:合并操作,减少重复渲染

在真实的交互场景中,同一时刻可能有多个数据变化:

data.name = 'Alice';
data.age = 30;
data.active = true;

如果每改一个数据就立刻去更新对应的 DOM 节点,浏览器会进行多次重排、重绘,性能消耗巨大且可能造成页面闪烁。虚拟 DOM 配合 Vue 的异步更新队列,会将多次数据变更对应的虚拟 DOM 更新全部收集起来,在一个异步任务(微任务)中一次性完成新旧虚拟 DOM 的比较(Diff),然后只对发生变化的几个真实 DOM 节点做一次性更新。这样,无论你在一帧里改了多少次数据,最终只产生一次 DOM 操作批次,渲染性能得到有效保障。

降低 DOM 操作开销:只改该改的,不改不该改的

真实 DOM 的访问和修改代价高昂——读取一个 offsetWidth 都可能触发浏览器回流。虚拟 DOM 将对比过程完全放在轻量的 JavaScript 对象上进行,然后计算出最小的必要 DOM 操作指令集

举个例子,一个列表中有上千个 <li>,当你只修改了其中一条数据时:

  • 传统手动拼接 HTML 的方案可能会 innerHTML 全部重写,导致整个列表销毁再创建,丢失输入框焦点、滚动位置,且触发大量 DOM 操作。
  • 虚拟 DOM 通过 Diff 算法定位到真正变化的那一个 <li>,然后仅对该节点执行一次 textContent 更新。其余节点完全复用,原封不动。

这种精确的差异更新,把 DOM 操作从“重刷”变成了“微调”,极大降低了浏览器的渲染负担。而且,虚拟 DOM 的差异计算本身很快,因为 Vue 的编译器在构建时就已经标记好了静态与动态节点,Diff 时直接跳过静态节点,只对比带 PatchFlag 的动态内容,进一步减少了对比范围。

一句话总结:虚拟 DOM 提供了一个轻量、平台无关的界面描述层,通过批量延迟更新和精准差异计算,让渲染既高效又灵活。你写的仍然是声明式的模板,而虚拟 DOM 在底层默默做了“如何省力地更新界面”的脏活累活。