建材网站建设,品牌策略的7种类型,网站建设创新能力痛点,百度指数app官方下载提示#xff1a;文章写完后#xff0c;目录可以自动生成#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、diff算法是什么#xff1f;二、vue2中的diff算法三、vue3中的diff算法总结 前言
一、diff算法是什么#xff1f;
diff算法很早就存在了#xff0c;一开… 提示文章写完后目录可以自动生成如何生成可参考右边的帮助文档 文章目录 前言一、diff算法是什么二、vue2中的diff算法三、vue3中的diff算法总结 前言
一、diff算法是什么
diff算法很早就存在了一开始diff算法是用来计算出两个文本的差异。所以大家一定要明确diff算法并不是react或者vue原创的它们只是用diff算法来比较两个vnode的差异并且只针对该部分进行原生DOM操作而非重新渲染整个页面。而在vue里在更新虚拟DOM的在patch(vnode, newVnode)方法中比较新旧函数时会用到diff。
二、vue2中的diff算法
vue2中使用的diff算法是双端比较以下是vue2中实现diff的主要步骤。
function vue2Diff(prevChildren, nextChildren, parent) {let oldStartIndex 0,oldEndIndex preChildren.length - 1,newStartIndex 0,newEndIndex nextChildren.length - 1;let oldStartNode prevChildren[oldStartIndex],oldEndNode prevChildren[oldEndIndex],newStartNode nextChildren[newStartIndex],newEndNode nextChildren[newEndIndex];while (oldStartIndex oldEndIndex newStartIndex newEndIndex) {// 头头 尾尾 头尾 尾头if (oldStartNode.key newStartNode.key) {patch(oldStartNode, newStartNode, parent);oldStartIndex;newStartIndex;oldStartNode prevChildren[oldStartIndex];newStartNode nextChildren[newStartIndex];} else if (oldEndNode.key newEndNode.key) {patch(oldEndNode, newEndNode, parent);oldEndIndex--;newEndIndex--;oldEndNode prevChildren[oldEndIndex];newEndNode nextChildren[newEndIndex];} else if (oldStartNode.key newEndNode.key) {patch(oldStartNode, newEndNode, parent);parent.insertBefore(oldStartNode.el, oldEndNode.el.nextSibling);oldStartIndex;newEndIndex--;oldStartNode prevChildren[oldStartIndex];newEndNode nextChildren[newEndIndex];} else if (oldEndNode.key newStartNode.key) {patch(oldEndNode, newStartNode, parent);// 把老的最后一个节点挪到最前面parent.insertBefore(oldEndNode.el, oldStartNode.el);oldEndIndex--;newStartIndex;oldEndNode prevChildren[oldEndIndex];newStartNode nextChildren[newStartIndex];} else {// 四次比较都没有比较到// 拿着新的newStartNode中的当前的key遍历prevChildren找有没有相同的keylet newkey newStartNode.key,oldIndex prevChildren.findIndex((child) child.key newKey);if (oldIndex -1) {// 匹配到了let oldNode prevChildren[oldEndIndex];patch(oldNode, newStartNode, parent);parent.insertBefore(oldNode.el, oldStartNode.el);prevChildren[oldEndIndex] undefined; // 当前序号为oldIndex中置为空} else {// 没匹配到直接创建一个新的节点放在开头就好了mount(newStartNode.el, parent, oldStartNode.el);}newStartNode nextChildren[newStartIndex];}}if (oldEndIndex oldStartIndex) {for (let i newStartIndex; i newEndIndex; i) {mount(nextChildren[i]);}} else if (newEndIndex newStartIndex) {for (let i newStartIndex; i newEndIndex; i) {parent.remove(prevChildren[i]);}}
}双端比较的流程
头头比较首先是老的节点数组的头结点和新节点数组的头节点进行比较如果相同说明当前节点为发生变化无需修改虚拟DOM则老的节点和新的节点同时向后移动一位再进行比较如果不相同则启动尾尾比较。尾尾比较老的节点数组的尾结点和新节点数组的尾节点进行比较如果相同则两者都向前移动一位之后老的节点数组右移新节点数组左移再进行比较如果不相同则进行头节点和尾节点比较。头尾比较老的节点数组的头节点和新的节点数组的尾节点比较如果相同说明老的节点在新节点数组中被移动到最后一位了就把老的头节点放到最后再进行比较如果如果不相同则进行尾节点和头节点比较。尾头比较新的节点数组的头节点和老的节点数组的尾节点比较如果相同说明老的节点在新节点数组中被移动到第一位了就把老的头节点放到最开头之后老的节点数组左移新节点数组右移再进行比较如果前四步都走完还不匹配则进入else的兜底判断。拿着新的新节点的当前的key遍历老节点数组找有没有相同的key如果有则把老数组中的对应节点放到新的数组中对应key值的Index处如果没匹配到就直接创建新节点。
三、vue3中的diff算法
vue3中的diff算法相比起vue2的双端比较进行了升级通过求得最长递增子序列此处用到了贪婪算法和二分查找优化效率使得diff效率更高。 // vue-next/packages/runtime-core/src/renderer.ts/patchKeyedChildren 中const patchKeyedChildren (oldChildren, // 旧的一组子节点newChildren, // 新的一组子节点) {let i 0// 新的一组子节点的长度const newChildrenLength newChildren.length// 旧的一组子节点中最大的 indexlet oldChildrenEnd oldChildren.length - 1// 新的一组子节点中最大的 indexlet newChildrenEnd newChildrenLength - 1// 1. 自前向后比对while (i oldChildrenEnd i newChildrenEnd) {const oldVNode oldChildren[i];const newVNode newChildren[i];if (isSameVNodeType(oldVNode, newVNode)) {patch(oldVNode, newVNode);} else {break;}i;}// 2. 自后向前比对// 旧的一组子节点中最大的 indexlet oldChildrenEnd oldChildren.length - 1;// 新的一组子节点中最大的 indexlet newChildrenEnd newChildrenLength - 1;while (i oldChildrenEnd i newChildrenEnd) {const oldVNode oldChildren[oldChildrenEnd];const newVNode newChildren[newChildrenEnd];if (isSameVNodeType(oldVNode, newVNode)) {patch(oldVNode, newVNode, container, null);} else {break;}oldChildrenEnd--;newChildrenEnd--;}// 3. 新节点多于旧节点挂载多的新节点if (i e1) {if (i e2) {...}}// 4. 新节点少于旧节点卸载多的旧节点else if (i e2) {while (i e1) {...}}// 5. 乱序else {...}}vue3中的diff算法大致分为五步
头序比较算法老节点数组和新节点数组的头结点相互比较如果相同则同时后移直到比较到发现不同的情况为止此时启动尾序比较算法。尾序比较算法即老节点数组和新节点数组的尾结点相互比较如果相同则同时前移也是直到比较到发现不同的情况为止。判断此时如果新节点数组中有节点但老节点数组中没有节点则创建一个新节点插入对应位置。如果老节点数组中存在的节点在新的节点数组中不存在则卸载掉老的节点。上述四步结束过后就会剩下一些乱序节点即节点既存在老的节点数组又存在于新的节点数组只不过节点所在的位置发生了变化因此只需要找到老节点在新节点数组中的位置并把它移动过去就可以了也就是在这里vue3使用了最长递增子序列先固定一个最长的不需要移动的数组在把不在这个最长递增子序列中的节点外的乱序节点插入其中以实现最小的移动消耗就能实现最终的效果。至于vue3中实现最长递增子序列的过程可以参考我之前的 vue3源码中的最长递增子序列的实现方式这里就不再赘述了。
总结
本文简单介绍了vue2和vue3中diff算法的大致实现流程具体更详尽的代码建议看vue的源码里面还有很多细节。