虚拟DOMVirtualDOM

一、虚拟 DOM & Diff 专有词

虚拟 DOM 节点结构

就是一个普通 JS 对象,只有三个关键字段:

 
{
 
    type: 'div',           // 什么标签 / 什么组件(函数/类)
 
    props: {               // 标签的属性(id, className, style...) + children
 
        id: 'box',
 
        className: 'red',
 
        children: [...]    // 子节点(React 17+ 把 children 归到 props 里了)
 
    }
 
}
 

为什么不用真实 DOM? 真实 DOM 一个 <div> 背后挂着 200+ 个属性和方法,全部是 C++ 层映射过来的。创建 1000 个真实 DOM = 20 万个属性。创建 1000 个上面的 JS 对象 = 3000 个字段——差了两个数量级。


Diff 算法

Diff = Difference(差异)。React 比较新旧两棵虚拟 DOM 树,找出”哪些节点需要更新”的算法。

暴力算法为什么是 O(n³):两棵树逐节点全量比较,每个节点还要和另一棵树的每个节点比较——三层循环。

O(n) 是什么大 O 表示法(Big O Notation)——衡量算法”数据量变大时,执行时间怎么增长”的符号。不关心具体秒数,只关心”增长趋势”。

复杂度1000 个节点的操作量通俗理解
O(n³)10 亿次你清点全班作业——把每个人的作业和所有人的作业都比一遍,再和所有人的作业的每一页都比一遍。3 层循环嵌套
O(n²)100 万次两个人对答案——你的每一题和对方的每一题都比。2 层循环
O(n)1000 次一人发一张卷子——从前往后传,每人经手一次。1 层循环
O(1)1 次直接喊名字——不管多少人都是一声。不循环

React 把 Diff 从 O(n³) 压到 O(n) = 从”10 亿次比较”压到”1000 次比较”——差了一百万倍。靠的不是黑科技,而是三个大胆但合理的假设(见正文 1.4 节)。


key

列表渲染时给每个节点贴的”身份证号”,React 用它识别节点身份。

 
// 没有 key:React 只能靠"位置"(第0个、第1个)猜谁是谁
 
// 删掉第一项 → 第0个位置还是有人 → React 以为没删,只是内容变了 → 浪费
 
// 有 key:React 凭 id 认人
 
// 删掉 id=1 → React 知道"id=1 走了,id=2 和 id=3 还在,不用重新渲染它们"
 

为什么不能用 index(下标)? 因为下标不是”这个人的身份”,而是”这个人的临时座位号”。删掉第一个人,后面所有人的座位号都变了——身份证号不会因为换座位而变。


递归(Recursion)

函数自己调自己,用来处理”嵌套结构”(树、文件夹、DOM 树)。

 
# Python 里你见过:
 
def factorial(n):
 
    if n == 1:
 
        return 1
 
    return n * factorial(n - 1)  # 自己调自己
 

React 的 Diff 是递归的——比较完父节点,对每个子节点再用同样的 Diff 逻辑。因为 DOM 树天然是嵌套结构,递归是最自然的处理方式。


子树(Subtree)

DOM 树中,以某个节点为根的这一整段分支。

 
        body                  ← 整棵树
 
       /    \
 
    div     span               ← div 是一棵子树(根是div,包含它下面所有后代)
 
    / \       \
 
   p  button   a               ← p 也是一棵子树(根是p,只包含它自己,因为没子节点)
 

React 的 Diff 假设②:如果节点类型变了(<div><span>),整棵子树连根拔起重建,不和旧子节点比较。


速记卡(面试闪卡)

Q1:一句话讲清「虚拟DOMVirtualDOM」到底是什么?

A:虚拟 DOM 是轻量 JS 对象,Diff 算法把树比对降到线性 O(n)。

Q2:虚拟 DOM 节点结构 —— 怎么理解? —— 怎么理解?

A:像一张轻薄的快递单(只有 type、props、children 三个字段),而真实 DOM 是背后挂 200+ 属性的大箱子。1000 个 JS 对象才 3000 字段,差了两个数量级。英语:Virtual DOM node。

Q3:Diff 算法 O(n³)→O(n) —— 怎么理解? —— 怎么理解?

A:像清点全班作业:野蛮比法是每人作业和所有人每页比(10 亿次),React 靠”同层比、按 type 整棵换”的假设压到一人发一张卷(1000 次),快了一百万倍。英语:Diff / Big O。

Q4:key 的作用 —— 怎么理解? —— 怎么理解?

A:像给每个学生发”身份证号”而非”临时座位号”。删掉第一个人,座位号全乱、React 误判;凭 id 认人,剩下的不用重渲染。别用 index 当下标。英语:key / reconciliation。

Q5:递归与子树 —— 怎么理解? —— 怎么理解?

A:像公司组织架构,Diff 比较完部门(父节点)就对每个小组(子节点)用同一套逻辑递归下去;一旦某节点 type 变了,整棵子树连根拔起重建。英语:recursion / subtree。

Q6:核心速记主线有哪些?

  • 虚拟 DOM 是极简 JS 对象(type+props+children),比真实 DOM 轻两个数量级

  • Diff 靠三假设把复杂度从 O(n³) 压到 O(n),快百万倍

  • key 是节点身份证,用 id 别用 index,避免误删重渲染

  • Diff 递归进行;type 变了整棵子树重建

口诀

A:虚拟节点三字段,

Diff 三层压成线;

key 是身份莫用号,

子树变型整根换。

相关链接