人人都会AI编程

4.1 虚拟 DOM 的本质:JavaScript 对象描述 UI

更新时间:2026-07-10

虚拟 DOM 并不是一个神秘的黑盒,它的本质就是一个用 JavaScript 对象对真实 DOM 的轻量级描述。你可以把它理解为 UI 的“蓝图”:一个嵌套的、包含类型、属性和子节点信息的纯数据结构。因为它是纯粹的 JavaScript 对象,所以创建和操作它的成本远低于直接操作浏览器 DOM 节点。

从 JSX 到虚拟 DOM

当你在 React 中写下 JSX,它会被 Babel 等编译器转译为 React.createElement 调用,最终生成一组 JavaScript 对象。例如:

<div className="container">
  <h1>欢迎</h1>
  <p>这是一个段落</p>
</div>

编译后大致相当于:

React.createElement(
  'div',
  { className: 'container' },
  React.createElement('h1', null, '欢迎'),
  React.createElement('p', null, '这是一个段落')
)

执行这些 createElement 调用后,会得到一个嵌套的 JavaScript 对象,类似:

{
  type: 'div',
  props: {
    className: 'container',
    children: [
      {
        type: 'h1',
        props: { children: '欢迎' }
      },
      {
        type: 'p',
        props: { children: '这是一个段落' }
      }
    ]
  }
}

这个对象就是虚拟 DOM 树。它的结构非常清晰:

  • type:节点类型。对于原生 DOM 元素,它是字符串(如 'div''h1');对于函数/类组件,它是对应的组件函数或类本身。
  • props:节点属性,包括 classNamestyle、事件处理器等,以及特殊的 children 属性(子节点)。
  • children:子节点列表,可以是更多虚拟 DOM 节点、字符串、数字或布尔值。

为什么需要这样的对象描述

1. 创建成本极低

在浏览器中创建一个真实的 <div> 节点,需要调用 document.createElement('div'),这会返回一个重量级的 DOM 元素,上面挂载着数百个属性和方法。而虚拟 DOM 只是一个普通对象,在 JavaScript 引擎中生成和遍历的速度极快。

2. 便于跨平台抽象

虚拟 DOM 描述的是 UI 的结构和内容,不绑定到特定的渲染引擎。React 可以将同一棵虚拟 DOM 树渲染到不同的目标环境:

  • 浏览器 DOM(React DOM)
  • iOS/Android 原生组件(React Native)
  • Canvas(如 react-canvas)
  • 终端(如 ink)

这种抽象能力是 React “Learn Once, Write Anywhere”理念的技术基石。

3. 批量更新与 Diff 的基础

每次状态变化时,React 会生成一棵全新的虚拟 DOM 树,然后通过 Diff 算法比较新旧两棵树,计算出最小的更新操作,最后只将这些操作应用到真实 DOM。整个过程都在轻量级的对象层面完成,极大地减少了昂贵 DOM 操作的次数。

虚拟 DOM 不是银弹

需要明确的一点是:虚拟 DOM 并不能让所有场景都变得最快。在极简单的 UI 更新中,直接操作 DOM 可能更快。但虚拟 DOM 的核心价值在于:在中等及高复杂度的应用中,它能自动提供接近最优的批量更新策略,同时让开发者用声明式方式编写 UI,而不必手动优化 DOM。它是开发体验与性能之间的一个绝佳平衡点。

与真实 DOM 的对比

| 特性 | 虚拟 DOM | 真实 DOM |
|------|----------|----------|
| 本质 | 纯 JS 对象 | 浏览器引擎实现的 C++ 对象(在 JS 中表现为 DOM 接口) |
| 体积 | 轻量,只含关键信息 | 重量级,携带大量方法和属性 |
| 创建速度 | 极快,仅涉及对象分配 | 慢,需要调用浏览器 API 并初始化复杂内部状态 |
| 跨平台 | 天然跨平台,可映射到不同渲染器 | 只存在于浏览器环境 |
| 操作成本 | 极低,纯 JS 计算 | 高,每次操作可能触发回流/重绘 |

实际开发中如何看待虚拟 DOM

在日常开发中,你几乎不需要直接创建或操作虚拟 DOM 对象,因为 JSX 编译器已经帮你完成了转换。但理解虚拟 DOM 是什么,能帮助你真正弄懂以下问题:

  • 为什么状态更新后并不总是立即反映在页面上?(因为要经过 Diff 和批量更新)
  • 为什么元组、列表的 key 如此重要?(它帮助 Diff 算法识别节点身份)
  • 为什么 React 可以渲染到手机原生界面?(因为虚拟 DOM 只描述结构,渲染器决定具体绘制方式)

把虚拟 DOM 看作 UI 的“中间语言”——用极简的 JS 对象表达界面的当前状态,剩下的优化逻辑交给 React 去做。你只需专心书写组件,定义好数据到 UI 的映射关系即可。