答案是DOM diff算法通过比较新旧虚拟DOM树差异来最小化真实DOM操作。首先定义虚拟节点结构h函数,再实现render函数将虚拟节点转为真实DOM;diff函数处理五种情况:新节点不存在则删除、文本节点直接替换、标签不同替换元素、标签相同更新属性、递归对比子节点;最后通过实例展示1秒后更新视图的过程,体现了同层比较和最小化更新的核心思想。

DOM diff 算法是前端框架(如 React、Vue)用来高效更新视图的核心机制。它的目标是通过比较新旧虚拟 DOM 树的差异,最小化真实 DOM 操作,从而提升性能。下面是一个简化但可运行的 JavaScript DOM diff 算法实现,帮助你理解其基本原理。
我们先定义一个简单的虚拟 DOM 节点结构:
function h(tag, props, children) {
return { tag, props, children };
}
例如,创建一个虚拟节点:
const vnode = h('div', { id: 'app' }, [
h('p', {}, ['Hello']),
h('span', {}, ['World'])
]);
我们需要一个函数把虚拟节点转换成真实 DOM 节点:
立即学习“Java免费学习笔记(深入)”;
function render(vnode) {
if (typeof vnode === 'string' || typeof vnode === 'number') {
return document.createTextNode(vnode);
}
const el = document.createElement(vnode.tag);
// 设置属性
if (vnode.props) {
Object.keys(vnode.props).forEach(key => {
el.setAttribute(key, vnode.props[key]);
});
}
// 递归渲染子节点
if (vnode.children) {
vnode.children.forEach(child => {
el.appendChild(render(child));
});
}
return el;
}
diff 的主要逻辑是在更新时比较新旧 vnode,并修改真实 DOM。我们采用“同层比较”策略,不跨层级移动节点,这是主流框架的优化假设。
function diff(parentNode, oldVNode, newVNode) {
// 情况1:新节点不存在,删除
if (!newVNode) {
parentNode.remove();
return;
}
// 情况2:文本节点
if (typeof oldVNode === 'string' || typeof newVNode === 'string') {
if (oldVNode !== newVNode) {
parentNode.replaceWith(document.createTextNode(newVNode));
}
return;
}
// 情况3:标签不同,替换整个元素
if (oldVNode.tag !== newVNode.tag) {
parentNode.replaceWith(render(newVNode));
return;
}
// 情况4:标签相同,更新属性
const el = parentNode;
// 更新属性
const oldProps = oldVNode.props || {};
const newProps = newVNode.props || {};
Object.keys(oldProps).forEach(key => {
if (!newProps.hasOwnProperty(key)) {
el.removeAttribute(key);
}
});
Object.keys(newProps).forEach(key => {
if (oldProps[key] !== newProps[key]) {
el.setAttribute(key, newProps[key]);
}
});
// 情况5:递归对比子节点
const oldChildren = oldVNode.children || [];
const newChildren = newVNode.children || [];
// 简单按索引对比(无 key 优化)
const len = Math.max(oldChildren.length, newChildren.length);
for (let i = 0; i < len; i++) {
const oldChild = oldChildren[i];
const newChild = newChildren[i];
const childEl = el.childNodes[i];
// 新节点不存在,删除
if (!newChild && childEl) {
childEl.remove();
}
// 旧节点不存在,新增
else if (oldChild && !childEl) {
el.appendChild(render(newChild));
}
// 都存在,递归 diff
else if (oldChild && newChild) {
diff(childEl, oldChild, newChild);
}
}
}
结合以上代码,可以这样使用:
// 初始虚拟 DOM
const v1 = h('div', { id: 'test' }, [
h('p', {}, ['Hello']),
h('span', {}, ['Old'])
]);
// 更新后的虚拟 DOM
const v2 = h('div', { id: 'test', class: 'updated' }, [
h('p', {}, ['Hello']),
h('span', {}, ['New'])
]);
// 渲染并挂载
const container = document.getElementById('app');
container.appendChild(render(v1));
// 模拟更新
setTimeout(() => {
diff(container.firstChild, v1, v2);
}, 1000);
基本上就这些。这个实现展示了 diff 的核心思想:递归对比、最小化 DOM 操作、基于同层比较。实际框架中还会引入 key 来优化列表对比,以及批量更新、异步渲染等高级特性,但基础逻辑一致。
以上就是JavaScript DOM diff算法实现的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号