通过类比 DOM 与 iOS 的思考
什么是虚拟 DOM?
虚拟 DOM(Virtual DOM)是一种前端框架(如 React、Vue)中的 UI 渲染优化技术。它的本质是用 JavaScript 对象在内存中描述一棵 DOM 树,然后通过比对(diff)和最小化更新,批量把变化同步到真实 DOM。
用 iOS 和 Python 开发类比
虚拟 DOM 就像是前端的 状态快照和差异更新机制 。
类比 iOS UIKit
在 iOS UIKit 里,你常常需要频繁修改 UI(比如 TableView 的数据刷新)。
如果你每次数据变化都直接操作 UIKit 的每个 View,性能会变差,因为:
- UIKit 的视图树结构复杂,频繁的 addSubview、removeFromSuperview、setNeedsLayout 等操作会导致大量不必要的重排和重绘(Layout & Rendering)。
- 多次细粒度的 UI 操作会卡顿(比如 TableView reloadData vs. batch updates)。
虚拟 DOM 类似于这样一种机制:
- 你先在内存里“画”好你要的 UI 状态(就像先用 Model/数据结构描述 View 层级,而不立刻操作 View)。
- 框架帮你计算出“最小的 UI 差异(diff)”,然后一次性、高效地批量更新真实 UI 层。
- 这样就减少了不必要的 UI 更新,提升了性能。
类比 Python 的数据变更监听
假如你用 Python 写 GUI(比如 tkinter),如果每次变量变了都立刻重画组件,性能会很差。更好的办法是:
- 先用一个“状态快照”描述所有要变的地方(比如 dict 树结构)。
- 最后统一“commit”,只真正重绘有变动的组件。
虚拟 DOM 的工作原理
初次渲染
- React/Vue 会用 JS 对象结构描述真实 DOM(比如 div、ul、li),这就是虚拟 DOM 树。
- 渲染时,把虚拟 DOM 转成真实 DOM,插入页面。
数据更新(如 setState)
- UI 变了 → 生成新的虚拟 DOM 树(新的 JS 对象结构)。
- 框架用“Diff 算法”对比新旧虚拟 DOM,找出哪些节点变化了。
- 框架把最小的差异(patches)批量同步到真实 DOM。
- 性能大幅提升,避免了无谓的操作。
更直观的类比
前端虚拟DOM | iOS UIKit |
---|---|
JS 对象描述 DOM 结构 | Model/数据结构描述 View 层级 |
diff 算法对比新旧树 | 只 diff 需要刷新的 cell 或 View |
一次性 batch patch DOM | UITableView 的 batch updates |
减少不必要的重绘或重排 | 避免重复 layoutSubviews,提升流畅度 |
经典的虚拟 DOM 代码片段
1 | // 这就是一个虚拟 DOM 节点 |
更新时,React 会生成新的 vdom,再跟旧的比对,最后只操作有变动的 DOM 节点。
虚拟 DOM Diff 算法原理(以 React 为例)
虚拟 DOM diff 的核心目标是高效找到两棵树的差异,并生成最小的真实 DOM 更新。
为什么不能简单对比
DOM 是树结构。两个 DOM 树之间,节点位置和层级经常变化。如果用“暴力递归比较每个节点”,复杂度是 O(n³),性能很低。
React 的优化策略
React 用了“分层递归,局部对比”策略,大大降低了复杂度(接近 O(n))。主要假设:
- 同层节点之间是可以复用的。
- 不同类型的节点不会互相复用。
- 给定 key 时,只对比 key。
主要 Diff 步骤
类型不同,直接替换
如果新旧节点类型不同(如 <div>
vs <span>
),直接卸载旧节点、创建新节点。
类型相同,对比属性
类型一样时,对比 props(比如 class、style、事件),只 patch 变化的属性。
递归对比子节点(children)
- 如果 children 是字符串/数字,直接替换内容。
- 如果是数组(多个子节点),进行 key-based diff。
Keyed diff(最核心部分)
- 给每个子节点分配唯一 key(推荐开发者传入)。
- 先用新旧 key 映射,判断哪些节点复用、插入、删除、移动。
- 没有 key 时,默认按索引顺序比对(效率较低,容易产生不必要的移动)。
伪代码流程:
1 | def diff(oldNode, newNode, parentDom): |
diffChildren 的关键实现(keyed diff 简化版):
1 | def diffChildren(oldChildren, newChildren): |
为什么这样高效?
- 只对最小范围内发生变化的节点做操作。
- key 能避免大规模无意义的节点移动。
- 没有 key 时,React 只好用“顺序 diff”,容易引发性能下降和错误。
总结
- 虚拟 DOM 就是用 JS 对象“抽象”描述 UI 结构,和真实 DOM 解耦。
- 通过 diff 算法,减少频繁的直接 DOM 操作(就像 UIKit 里避免频繁、分散的 View 变动)。
- 让 UI 更新更高效、可维护、更容易跨平台(React Native 也是虚拟 DOM 的思想延伸)。