当前位置: 首页 > Web前端 > vue.js

【笔记】Vue-3-Diff算法分析

时间:2023-03-31 17:52:10 vue.js

VirtualDOM为什么要用VirtualDOM?使用收到的更改通知生成一个新的虚拟DOM树。新旧diffpatched,减少直接通过DOMAPI增删改查DOM,提高开发效率。计算机科学中的所有问题都可以通过另一个间接层次来解决软件开发中的所有问题都可以通过添加额外的抽象层次来解决。(关注点分离)VirtualDOM是分层思维的体现。该框架将DOM抽象为VirtualDOM,可以应用于各个终端。Diff策略1.Diffbytreelevel(逐级差异)很少出现在WebUI中level会因交互而更新。按级别在新旧节点之间执行diff2。不同类型的节点往往差别很大。为了提高效率,只会对相同类型的节点进行diff。不同的类型会直接创建新的。类型节点,替换旧的类型节点下图中,上层图形改为下层图形。与同层相比,第二列五角星与三角形不同。虽然子节点的两个五芒星是一样的,但是三个五芒星会被直接销毁,换上新的节点。3.ListDiffSetkeys用于列表元素,可以提高效率。Diff流程参考文章:Vue的Diff算法详解updateChildren(parentElm,oldCh,newCh){letoldStartIdx=0,newStartIdx=0letoldEndIdx=oldCh.length-1letoldStartVnode=oldCh[0]letoldEndVnode=oldCh[oldEndIdx]letnewEndIdx=newCh.length-1letnewStartVnode=newCh[0]letnewEndVnode=newCh[newEndIdx]letoldKeyToIdxletidxInOldletelmToMoveletbeforewhile(oldStartIdx<=oldEndNewStartIdx&&<=newEndIdx){if(oldStartVnode==null){//对于vnode.key的比较,oldVnode=nulloldStartVnode=oldCh[++oldStartIdx]}elseif(oldEndVnode==null){oldEndVnode=oldCh[--oldEndIdx]}elseif(newStartVnode==null){newStartVnode=newCh[++newStartIdx]}elseif(newEndVnode==null){newEndVnode=newCh[--newEndIdx]}elseif(sameVnode(oldStartVnode,newStartVnode)){patchVnode(oldStartVnode,newStartVnode)oldStartVnode=oldCh[++oldStartIdx]newStartVnode=newCh[++newStartIdx]}elseif(sameVnode(oldEndVnode,newEndVnode)){patchVnode(oldEndVnode,newEndVnode)oldEndVnode=oldCh[--oldEndIdx]newEndVnode=newCh[--newEndIdx]}elseif(sameVnode(oldStartVnode,newEndVnode)){patchVnode(oldStartVnode,newEndVnode)api.insertBefore(parentElm,oldStartVnode.el,api.nextSibling(oldEndVnode.el))oldStartVnode=oldCh[++oldStartIdx]newEndVnode=newCh[--newEndIdx]}elseif(sameVnode(oldEndVnode,newStartVnode)){patchVnode(oldEndVnode,newStartVnode)api.insertBefore(parentElm,oldEndVnode.el,oldStartVnode.el)oldEndVnode=oldCh[--oldEndIdx]newStartVnode=newCh[++newStartIdx]}else{//使用key时的比较if(oldKeyToIdx===undefined){oldKeyToIdx=createKeyToOldIdx(oldCh,oldStartIdx,oldEndIdx)//有key生成索引表”(elmToMove.sel!==newStartVnode.sel){api.insertBefore(parentElm,createEle(newStartVnode).el,oldStartVnode.el)}else{patchVnode(elmToMove,newStartVnode)oldCh[idxInOld]=nullapi.insertBefore(parentElm,elmToMove.el,oldStartVnode.el)}newStartVnode=newCh[++newStartIdx]}}}if(oldStartIdx>oldEndIdx){before=newCh[newEndIdx+1]==null?null:newCh[newEndIdx+1].eladdVnodes(parentElm,before,newCh,newStartIdx,newEndIdx)}elseif(newStartIdx>newEndIdx){removeVnodes(parentElm,oldCh,oldStartIdx,oldEndIdx)}}updateChildren代码分分析循环成立条件:OldStart小于等于OldEnd&&NewStart小于等于NewEnd判断VNode是否为null,如果是指向下一个节点的指针,则跟随OS和NS(S|)、OE与NE(E|)、OS与NE(\)、OE与NS(/)比较判断是否相同S|节点位置不变,指针+1E|节点位置不变,指针-1\OldStart移动到OldEnd,OldStart+1,NewEnd-1/OldEnd移动到OldStart,NewStart+1,OldEnd-1之前如果不同,根据OldStart和OldEnd生成索引表key,检查元素是否在OldStart和OldEnd之间,如果有,直接移动到OldStart,如果没有,创建之后,在移动到OldStart之前,判断如果NewStart>NewEnd,则表示新节点已经遍历,删除OldStart和OldEnd之间的DOM,如果OldStart>OldEnd,则表示已经遍历完旧节点,根据index向DOM中添加更多的新节点示例:如图,灰色表示VirtualDOM,深色color表示realDOM四个指针OldStartIdx老起始节点OldEndIdx和老结束节点NewStartIdx新起始节点NewEndIdx新结束节点判断OldStartIdx和NewStartIdx是否相同1和1相同,两个Start指针都向右移动一位(+1)并继续比较OldStartIdx和NewStartIdx是否相同。2和5不一样,而是比较OldEndIdx和NewEndIdx,6和6一样,两个End指针都向左移动一位(-1)。5和2不一致,改成比较OldStartIdx和NewStartIdx2和5不同,改成比较OldEndIdx和NewEndIdx,也不同。而是比较OldStartIdx和NewEndIdx(方向)。2和2一样,将OldStartIdx对应的真实DOM移到OldEndIdx后面,同时OldStartIdx右移一位,NewEndIdx左移一位。继续比较Start,不一样,比较End,也不一样。比较OldStart和NewEnd,不同。比较OldEnd和NewStart(/方向),相同。将OldEnd对应的真实DOM移到OldStart之前。同时,OldEnd向左移动,NewStart向右移动。继续循环,Start|End|/四个方向都不一样。根据key生成OldStart和OldEnd之间的索引表,查找7是否在OldStart和OldEnd之间。如果找到,直接移动到OldStart之前。如果找不到,说明是新节点。VirtualDOM生成真实DOM后,将7移到OldStart前面。NewEnd向左移动,此时NewEnd小于NewStart,结束循环,删除OldStart和OldEnd之间的部分。新的VirtualDOM已经存在,DOM节点也已经生成。销毁旧的VirtualDOM列表并设置key后,不要遍历。算法的复杂度是O(n),否则最坏的情况是$O(n^2)$vue2+没有完整的patch过程,在diff操作的时候同时进行节点操作,提高了加法,DOM节点删除、修改、查询时间效率

最新推荐
猜你喜欢