外观
虚拟 DOM 解决了什么?
虚拟 DOM 是用普通对象描述界面。状态变了就生成一棵新树,和旧树做 diff,再把差异落到真实 DOM 上。它换来的是声明式更新和跨平台,不是「比手写 DOM 更快」。
原理
一次更新大致是:数据变化 → 重新描述 UI → 比较前后两棵树 → 只改变了的节点。
React 的比较有两条实用规则:
- 只比较同一层。两个节点类型不同,就拆掉整棵旧子树,挂上新的,不再试图复用里面的 DOM。
- 列表用
key标出同一项。key稳定时,移动一项只是移动 DOM;用数组下标当key时,插入或排序会把状态错接到别的项上。
jsx
{list.map((item) => (
<Row key={item.id} item={item} />
))}Vue 3 也有虚拟 DOM,但会跳过编译期就能确定的静态节点,并用补丁标志标记动态绑定,减少运行时比较。列表乱序移动时,它用最长递增子序列尽量少挪节点。
手写 DOM 可以在你完全知道哪一个节点变了的时候更省。虚拟 DOM 的成本是创建对象和比较;收益是大多数业务不用自己找节点、跨端可以换成另一套渲染器。
追问
key 为什么不要用下标?
下标描述的是位置,不是身份。在前面插入一项后,原来下标 0 的组件会拿到新数据,输入框里的状态却还留在这个组件实例上。用数据自己的 id。
和「直接改 DOM」比,什么时候虚拟 DOM 更亏?
超大列表每次都全量描述,又没有窗口化,比较本身会很贵。这种场景用虚拟列表,只描述可见项。见 无限列表。
易错点
- 虚拟 DOM 不是真实 DOM 的缓存副本,它是下一次渲染的描述。
key要在兄弟之间稳定且唯一,不需要全局唯一。- 类型变了就不会复用子树。把
div换成section会卸掉里面的状态。