虚拟DOM的diff算法通过同层比较、类型判断和key机制高效更新真实DOM。1. 算法基于只比对同层级、类型不同则替换、key识别可复用节点三大假设,避免高复杂度。2. 核心过程包括节点增删、类型变更替换及属性更新,并递归处理子节点。3. 子节点比对时利用key建立映射,结合最长递增子序列计算最小移动,提升复用效率。4. 辅助函数负责创建元素与同步属性,整体策略在性能与准确性间取得平衡,适用于多数场景。

虚拟DOM的diff算法核心目标是高效地更新真实DOM。直接操作DOM成本高,频繁修改会导致页面重绘和回流。通过在JavaScript中维护一个轻量的虚拟DOM树,React等框架能在状态变化时先比对新旧虚拟树,找出最小变更集,再批量更新真实DOM。
1. diff策略与假设
为降低复杂度,diff算法基于三个关键假设:
-
只做同层比较:不同层级的节点被替换而非移动。这避免了跨层级对比带来的O(n³)复杂度。
-
类型不同的节点生成完全不同结构:如果新旧节点类型不同(如div变span),直接销毁旧节点并创建新节点。
-
通过key识别可复用的子节点:列表中的元素使用key属性帮助算法判断哪些节点可以复用、移动或删除。
2. 核心diff过程实现
以下是简化版diff算法的核心逻辑,模拟React的部分行为:
function diff(oldVNode, newVNode, parentEl) {
if (!oldVNode && !newVNode) return;
if (oldVNode && !newVNode) return parentEl.removeChild(oldVNode.el);
if (!oldVNode && newVNode) return parentEl.appendChild(createElement(newVNode));
// 类型不同,替换整个节点
if (oldVNode.type !== newVNode.type) {
parentEl.replaceChild(createElement(newVNode), oldVNode.el);
return;
}
// 类型相同,更新属性
const el = newVNode.el = oldVNode.el;
updateProps(el, oldVNode.props, newVNode.props);
// 子节点diff
if (Array.isArray(oldVNode.children) || Array.isArray(newVNode.children)) {
diffChildren(el, oldVNode.children, newVNode.children);
}
}
3. 子节点比对优化(带key)
子节点列表的比对是性能关键。不带key时只能按索引一一对应,带key后可精准复用:
立即学习“Java免费学习笔记(深入)”;
- 遍历新列表,用key建立映射表
- 扫描旧列表,标记已复用节点
- 未复用的旧节点删除,未匹配的新节点插入
- 利用最长递增子序列(LIS)计算最少移动方案
例如:
// 旧: [A, B, C] → 新: [C, A, B]
// key映射: C→0, A→1, B→2
// 旧节点位置: A(0), B(1), C(2)
// 按新顺序取索引: [2, 0, 1] → LIS: [0,1] → 只需移动C到开头
4. 节点创建与属性更新
辅助函数负责创建真实元素和同步属性:
function createElement(vnode) {
const el = document.createElement(vnode.type);
updateProps(el, {}, vnode.props);
vnode.children.forEach(child => {
el.appendChild(createElement(child));
});
vnode.el = el;
return el;
}
function updateProps(el, oldProps, newProps) {
for (let name in oldProps) {
if (!(name in newProps)) el.removeAttribute(name);
}
for (let name in newProps) {
el.setAttribute(name, newProps[name]);
}
}
基本上就这些。diff算法本质是在“精确性”和“性能”之间权衡,React选择同层比较+key机制,在大多数场景下既快又够用。
以上就是JavaScript虚拟DOM_diff算法核心实现的详细内容,更多请关注php中文网其它相关文章!