diff 算法在 React 项目中的应用
经常看到面试题目中介绍, React 框架的核心优势就是 虚拟DOM 和 diff 算法确保高效率渲染,那么 React 中具体 diff 是怎么实现的呢?
具体深入的 diff 算法,很多论文博客都有介绍。现在也有很多博客会《深入阅读React源码学习框架等》。我觉得从框架使用者的角度,不需要深入研究到这么深。可以用简单的图示和文字,把深入的知识说明白,才是学习的目的。
下面结合资料,按照我的理解,简单谈谈 diff 算法的具体逻辑
React 虚拟 DOM 的 diff 算法
React 的 diff 算法是一种用于比较虚拟 DOM 树的两个版本之间差异的高效算法。它是 React 用于更新 DOM 树的核心部分,以便只更新实际 DOM 中改变的部分,而非每次都重新渲染整个应用。
简单的说,就是当数据变化后,diff 算法可以高效的实现 dom 的更新。
React diff 算法的核心是递归比较两棵树的节点差异。
// 比较前后 dom 树
function diff(oldTree, newTree) {
// 比较类型,如果类型不同,直接替换节点
if (oldTree.type !== newTree.type) {
replaceNode(oldTree, newTree);
return;
}
// 比较属性
const attrDiff = diffAttributes(oldTree.attr, newTree.attr);
if (attrDiff) {
applyAttributes(oldTree, attrDiff); // 如果属性不同,应用属性差异
}
// 递归比较子节点
diffChildren(oldTree.children, newTree.children);
}
function diffChildren(oldChildren, newChildren) {
}
function replaceNode(oldNode, newNode) {
}
function diffAttributes(oldAttrs, newAttrs) {
// 比较节点属性差异
}
function applyAttributes(node, attrDiff) {
// 应用属性差异到指定节点
}
第一种情况
DOM元素不同:这种情况肯定是销毁重建,因为按顺序Diff,DOM元素发生了变化:p-span,span-p
第二种情况
DOM元素不同,但相同元素设置了Key:当我们设置了key属性的时候,情况就发生了变化,Diff算法会依赖于key查找对比,发现虽然它们的位置发生了变化,但内容没有发生变化,后续只需要移动DOM节点即可,不需要销毁重建!
所以,列表渲染时必须使用稳定的 id 作为 key,才能确保高效的 diff 重新渲染。
第三种情况
同key的移动、删除、新增算法:对于同一层级同一类型元素,标注了相同key的Diff,就是React的Diff算法最精华聪明之处,可以识别出来组件本身是移动、新增、删除,而不需要按顺序对比导致大量的销毁与DOM重建的工作。(需仔细体会mountIndex和lastIndex的对比)。