虚拟 DOM 与 Diff 算法
虚拟 DOM 是真实 DOM 的 JS 对象描述({ tag, props, children }),一次 JS 计算远比操作真实 DOM 便宜:框架先 diff 虚拟树得到最小修改集,再一次性 patch 到真实 DOM。它的价值不是“比直接操作 DOM 快”,而是“声明式编程 + 跨平台(RN、SSR) + 批量更新”。
Diff 算法(以 React 为例)三句话:同层比较(不跨层级移动);类型不同直接替换整棵子树;同类型按 key 复用节点,key 是节点身份标识。Vue 的 diff 更进一步:双端比较(头头、尾尾、头尾、尾头四种快速路径)+ 最长递增子序列减少移动。key 用 id 而不是 index 是老生常谈考点:index 做 key 会导致组件状态错配与无谓重渲染。
- 虚拟 DOM = JS 对象描述视图;价值是声明式、跨平台、批量 patch,而非绝对更快
- Diff 三规则:同层比较、类型不同整树替换、按 key 复用
- key 要稳定唯一(用 id);index 做 key 在列表增删时会制造 bug
JS
// 手写 vnode + 简化 diff(面试概念演示) function h(tag, props, children) { return { tag, props, children: children || [] }; // 虚拟节点 } function patch(parent, oldV, newV, index = 0) { const el = parent.children[index]; // 1. 新节点不存在:删除 if (!newV) { if (el) parent.removeChild(el); return; } // 2. 都不存在:新建 if (!oldV) { parent.appendChild(render(newV)); return; } // 3. 标签不同(或 key 不同):整体替换 if (oldV.tag !== newV.tag) { parent.replaceChild(render(newV), el); return; } // 4. 同标签:更新属性与文本,递归 children updateProps(el, oldV.props, newV.props); const max = Math.max(oldV.children.length, newV.children.length); for (let i = 0; i < max; i++) { patch(el, oldV.children[i], newV.children[i], i); } } function render(v) { const el = document.createElement(v.tag); Object.entries(v.props || {}).forEach(([k, v]) => (el[k] = v)); v.children.forEach(c => el.appendChild(typeof c === "string" ? document.createTextNode(c) : render(c))); return el; } function updateProps() {} // 省略:diff props 细节 // 验证:列表更新只 patch 差异 const root = document.createElement("ul"); const oldList = h("ul", {}, [h("li", { key: "a" }, ["A"]), h("li", { key: "b" }, ["B"])]); const newList = h("ul", {}, [h("li", { key: "b" }, ["B"]), h("li", { key: "c" }, ["C"])]); patch(root, null, oldList); // 首次渲染 patch(root, oldList, newList); // 更新:同 key 复用,新增 C,删除 A console.log("最终 DOM:", root.innerHTML);
本节要点虚拟 DOM 是声明式渲染的中间层;diff 就三规则 + key 复用,key 用稳定 id 不用 index。




