时间:2021-05-25
双端比较算法是vue2.x采用的diff算法,本篇文章只是对双端比较算法粗略的过程进行了一下分析,具体细节还是得Vue源码,Vue的源码在这
假设当前有两个数组arr1和arr2
let arr1 = [1,2,3,4,5]let arr2 = [4,3,5,1,2]那么其过程有五步
每次比较都是从数组的两端开始比较,如果是首位比较相等,那么比较的开头索引+1
如果是在末尾比较成功,那么比较的结束索引-1,当开头索引大于结束索引时说明比较已经结束
用一个gif图来表示
我们发现,上面的算法走完后,如果新旧两个数组只是顺序变化,那么它能完美的diff出差异,但是如果新数组有新增或者删除的时候就不行了,因此我们在while循环完成后需要找出新增或者删除的元素,那怎么知道哪些是新增哪些是删除的元素呢?
在比较的第五步,选取的新数组的第一个元素和旧数组的所有元素逐一对比,这里我们就可以得出了这个数组是否是新增,如果对比相等,那就是位置变换,否则当前元素就是新增的,但是,while循环的条件是oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx,如果是以下情况
let arr1 = [1,2,3,4,5]let arr2 = [1,2,3,4,5,6,7]因为循环条件的导致,这里会在5次while后就结束了,因此在数组末尾的6和7永远走不了第五步的插入条件,那如何判断6和7是新增的呢?我们来观察一下while循环结束后的索引
//例子1let arr1 = [1,2,3,4,5]let arr2 = [1,2,3,4,5,6,7]//diff后它们的索引为oldStartIdx = 5, oldEndIdx = 4newStartIdx = 5, newEndIdx = 6//例子2let arr1 = [1,2,3,4,5]let arr2 = [4,5,6,7,1,3,2]//diff后它们的索引为oldStartIdx = 3, oldEndIdx = 2newStartIdx = 6, newEndIdx = 5//例子3let arr1 = [1,2,3,4,5]let arr2 = [7,1,3,5,6,4,2]//diff后它们的索引为oldStartIdx = 5, oldEndIdx = 4newStartIdx = 4, newEndIdx = 4//例子4let arr1 = [1,2,3,4,5]let arr2 = [2,4,1,5,7,3,6]//diff后它们的索引为oldStartIdx = 3, oldEndIdx = 2newStartIdx = 6, newEndIdx = 6我们发现,新增元素的索引和newStartIdx还有newEndIdx是一一对应的
那么得出的结论就是,如果在while循环结束后,如果newStartIdx是小于或者等于newEndIdx,那么在newStartIdx和newEndIdx索引之间对应的元素就是新增的元素,并且oldStartIdx总是比oldEndIdx大
上面说完了新增,那如果是删除元素呢?看例子
//例子1let arr1 = [4,3,5,6,7,2,1]let arr2 = [1,3,5,4,2]//diff后它们的索引为oldStartIdx = 3, oldEndIdx = 4newStartIdx = 3, newStartIdx = 2//例子2let arr1 = [7,2,3,5,6,1,4]let arr2 = [5,1,2,3,4]//diff后它们的索引为oldStartIdx = 0, oldEndIdx = 4newStartIdx = 4, newStartIdx = 3//例子3let arr1 = [1,5,4,2,6,7,3]let arr2 = [4,5,1,2,3]//diff后它们的索引为oldStartIdx = 4, oldEndIdx = 5newStartIdx = 4, newStartIdx = 3同理新增的观察套路,发现newStartIdx总是比newStartIdx大,并且需要删除的元素总是在oldStartIdx和oldEndIdx对应的索引之间,那么我们只需要把oldStartIdx和oldEndIdx的元素删除即可,那问题来了,像例子2 中oldStartIdx和oldEndIdx索引之间的元素有7,2,3,5,6其中真正需要删除的只有7和6,这样子不就误删了2,3,5么?关键的来了,我们看例子2的2,3,5发现它们走的都是双端比较算法的第五步,第五步写的代码是
const idxInOld = prevChildren.findIndex((node) => { if (node && node.key === newStartVNode.key) { return true } }) if (idxInOld >= 0) { prevChildren[idxInOld] = undefined } else { //newStartVNode是新元素 } newStartVNode = nextChildren[++newStartIdx]如果idxInOld>0说明在旧数组中找到了,那么我们将preChildren[idxInOld]设置为undefined,也就是说2,3,5经过diff算法后,它们在arr1中的值已经被替换为了undefined,这里也是就为什么在diff算法开始需要判断!oldStartVNode和!oldEndVnode的原因了,下面我们完善代码
function diff(prevChildren, nextChildren) { let oldStartIdx = 0 //旧数组起始索引 let oldEndIdx = prevChildren.length - 1 //旧数组结束索引 let newStartIdx = 0 //新数组其实索引 let newEndIdx = nextChildren.length - 1 //新数组结束索引 let oldStartVNode = prevChildren[oldStartIdx] let oldEndVNode = prevChildren[oldEndIdx] let newStartVNode = nextChildren[newStartIdx] let newEndVNode = nextChildren[newEndIdx] while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { if (!oldStartVNode) { //undefined 时前移一位 oldStartVNode = prevChildren[++oldStartIdx] } else if (!oldEndVNode) { //undefined 时后移一位 oldEndVNode = prevChildren[--oldEndIdx] } else if (oldStartVNode.key === newStartVNode.key ) { //1.开始与开始 oldStartVNode = prevChildren[++oldStartIdx] newStartVNode = nextChildren[++newStartIdx] } else if ( oldEndVNode.key === newEndVNode.key ) { //2.结束与结束 oldEndVNode = prevChildren[--oldEndIdx] newEndVNode = nextChildren[--newEndIdx] } else if (oldStartVNode.key === newEndVNode.key ) { //3.开始与结束 oldStartVNode = prevChildren[++oldStartIdx] newEndVNode = nextChildren[--newEndIdx] } else if (oldEndVNode.key === newStartVNode.key ) { //4.结束与开始 oldEndVNode = prevChildren[--oldEndIdx] newStartVNode = nextChildren[++newStartIdx] } else { //5.新数组开头元素和旧数组每一个元素对比 const idxInOld = prevChildren.findIndex((node) => { if (node && node.key === newStartVNode.key) { return true } }) if (idxInOld >= 0) { prevChildren[idxInOld] = undefined } else { //newStartVNode是新元素 } newStartVNode = nextChildren[++newStartIdx] } } if (oldStartIdx > oldEndIdx) { for (; newStartIdx <= newEndIdx; ++newStartIdx) { //新增内容 let vnode = nextChildren[newStartIdx] } } else if (newStartIdx > newEndIdx) { for (let i = oldStartIdx; i <= oldEndIdx; i++) { / /删除内容 } }}diff([1,2,3,4,5],[4,3,5,1,2])接下来我们使用两个gif图来表示一下diff过程
1.新增元素
2.减少元素
以上就是详解Vue2的diff算法的详细内容,更多关于Vue2的diff算法的资料请关注其它相关文章!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
本人对vue不是很懂,搜索了很多关于Vue2无限级分类介绍,下面我来记录一下,有需要了解Vue2无限级分类的朋友可参考。希望此文章对各位有所帮助。Vue树a{c
本文实例讲述了vue2嵌套路由实现方法。分享给大家供大家参考,具体如下:前面讲过了vue2路由基本用法,一般应用中的路由方式不会像上述例子那么简单,往往会出现二
前言使用过Vue和React的小伙伴肯定对虚拟Dom和diff算法很熟悉,它扮演着很重要的角色。由于小编接触Vue比较多,React只是浅学,所以本篇主要针对V
vue2经过2.2版本升级后,文件变成了8个:vue.common.jsvue.esm.jsvue.jsvue.min.jsvue.runtime.common
一、如何去除vue项目中访问地址的#vue2中在路由配置中添加mode(vue-cli创建的项目在src/router/index.js)exportdefau