You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
common sequence + unmount:同序列卸载;新节点在不破坏老节点顺序的基础上减少节点;
同样可以发现,不管是在头部删除,还是尾部删除元素,i和e2的关系始终满足i > e2;
if(i>e1){
...
}elseif(i>e2){// common sequence + unmount 老的多,新的少while(i<=e1){// 表示有需要删除的部分unmount(c1[i++]);}}
unknown sequence:乱序比对,diff中的核心算法,采用了最长递增子序列算法;
如上图虚线框中所示,这种乱序的该如何对比呢?
1). 以新节点建立元素key与下标索引的映射表;
// A B [C D E Q] F G// A B [E C D H] F G// i = 2, e1 = 5, e2 = 5consts1=i;consts2=i;// 1.根据新节点生成一个索引的映射表constkeyToNewIndexMap=newMap();for(leti=s2;i<=e2;i++){constnextChild=c2[i];keyToNewIndexMap.set(nextChild.key,i);}
组件更新
当依赖的属性变化时,会重新执行
effect
函数,我们再次调用render
方法生成新的虚拟DOM,进行diff
操作。元素更新流程
前后元素不一致
两个不同虚拟节点不需要进行比较,直接移除老节点,将新的虚拟节点渲染成真实DOM进行挂载即可。
前后元素一致
前后虚拟节点一样,则复用DOM元素,并且更新属性和子节点。
这其中又分为两种情况:老节点是文本和老节点是数组;如果老节点是数组,需要先删除老的子节点,否则直接替换;
将老子节点的父节点清空,然后挂载所有新子节点;
对于双方都是数组的情况,我们首先需要了解两个对比的方法:
sync from start
:从头开始一个个比,遇到不同的就停止;sync from end
:从尾开始一个个比,遇到不同就停止;根据上面两种方法比对新老子节点数组,有以下几种情况:
common sequence + mount
:同序列挂载;新节点在不破坏老节点顺序的基础上增加新的节点;可以发现,不管是从头部插入还是从尾部添加新的元素,
i
和e1
的关系始终满足i > e1
;common sequence + unmount
:同序列卸载;新节点在不破坏老节点顺序的基础上减少节点;同样可以发现,不管是在头部删除,还是尾部删除元素,
i
和e2
的关系始终满足i > e2
;unknown sequence
:乱序比对,diff中的核心算法,采用了最长递增子序列算法;如上图虚线框中所示,这种乱序的该如何对比呢?
1). 以新节点建立元素
key
与下标索引的映射表;2). 遍历老节点数组,删除新节点中没有的老节点,即Q节点;并更新新节点中存在的老节点的属性及子节点,同时标识出来且同新节点的下标联系起来。如何理解这句话,我们看图:
3). 经过之前的两步,我们已经更新了新老节点中都有的元素,剩下的工作只需要移动和新增元素。那么如何移动效率才最高呢?
Vue3
中采用算法求解最长递增子序列处理这个问题。接下来我们看看其原理(不深究其算法),是如何做的?假设,我们需要求解如上图所示数组的最长递增子序列,下面通过图示的方法展示算法的实现过程:
采用如上图所示的方式,从头到尾遍历完整个数组,最终我们会得到一个(贪婪模式下)递增序列的索引数组
seq
,与记录当一个节点索引位置的数组p
。整个的变化过程如下图所示:在得到了贪婪模式下的递增序列索引数组
seq
和记录上一个节点位置的p
后,就可以通过倒序查找的方式,得到我们需要的最长递增子序列。原理就是在知晓最后一个人,且每个人都知道自己的前一个人是谁,那么整个队列的顺序就可以始终如一,不管在队列中随机添加人员,都不会影响整个队列的顺序。4). 实现代码如下:
最后,通过求得的最长递增子序列完成新老子节点的更新:
The text was updated successfully, but these errors were encountered: