虚拟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 是身份莫用号,
子树变型整根换。