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
for (i = s2; i <= e2; i++)for循环遍历剩余c2,收集每个c2的元素的key,构成map => keyToNewIndexMap;
for (i = 0; i < toBePatched; i++)for循环遍历剩余c2部分长度来生成映射,并赋值为0;
for (i = s1; i <= e1; i++) for循环遍历剩余c1,使用key进行直接获取(for循环剩余c2进行获取)newIndex,此处证明还是要绑定好key,唯一性很重要;newIndex有值说明c2中存在当前老的元素在c1中,老的preChild,在c2中还需要,则调用patch;如果newIndex为undefined,则说明老的preChild在c2中不需要了,调用unmount,把当前preChild卸载掉;
遍历完剩余c1后,再倒着遍历剩余c2:for (i = toBePatched - 1; i >= 0; i--);如果(newIndexToOldIndexMap[i] === 0则证明当前nextChild为新的节点,调用patch;否则判断之前是否发生了移动moved,经过逻辑判断,调用move;
本篇文章将会着重讲解Vue3中数据发生更新时所做的事情。
例子代码
本篇将要讲解dom diff,那么咱们结合下面的例子来进行讲解,这个例子是在上一篇文章的基础上,加了一个数据变更,也就是list的值发生了改变。html中增加了一个按钮change,通过点击change按钮来调用change函数,来改变list的值。例子位于源代码
/packages/vue/examples/classic/
目录下,下面是例子的代码:源码解读
关于Vue3中数据发生变更,最终影响到页面发生变化的过程,我们本篇文章只对componentEffect以及以后的代码进行讲解,对于数据变更后,是如何执行到componentEffect函数,以及为何会执行componentEffect,后面的文章再进行讲解。
componentEffect
来看下componentEffect更新部分的代码:
当数据发生变化的时候,最终会走到上面的else的逻辑部分。
调用patch函数的过程,也就是根据VNode的type,走不同的支流的过程;点击change按钮:n1的值:
n2的值:
根据这个值,可以知晓,会走到processFragment函数;
processFragment
调用
processFragment(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized)
函数,参数的值:来看下processFragment函数的源码:
刨除掉first render的代码后,可以看到下面还是分为了两个分支;根据n1和n2可知,我们将会走if分支,执行patchBlockChildren。
patchBlockChildren
调用
patchBlockChildren(n1.dynamicChildren, n2.dynamicChildren, container, parentComponent, parentSuspense, isSVG)
函数,此时参数如下:来看下patchBlockChildren的源码:
可以看到patchBlockChildren是for循环调用patch函数,上面看到newChildren是一个长度为2的数组。循环遍历调用
patch(oldVNode, newVNode, container, null, parentComponent, parentSuspense, isSVG, true)
;processElement
咱们先说第二次循环,第二次比较简单;上面说到调用patch函数,通过上面了解到第二次循环newVNode的type是button;则会走到
processElement
,参数全部是透传过来的:如上代码,会直接调用patchElement,此时参数为:
patchChildren
现在再来说第一次循环,执行patch的时候,newVNode的type为Symbol(Fragment) => ul,此时还是会走到
processFragment
函数,不过此时的dynamicChildren为空,会继续运行到patchChildren
函数。patchChildren
此时运行到patchChildren函数,我们来看下运行到此时的参数:
下面看下patchChildren的源码:
此时patchFlag的值为128,同时我们的list渲染是有key的,so 会运行
patchKeyedChildren
函数,c1为四个li组成的数组(a,b,c,d);c2为新的li组成的数组(a,d,e,b);其他值透传到patchKeyedChildren。patchKeyedChildren
上面对patchKeyedChildren函数的参数已经进行了说明,在这里我们再回顾下:
接下来看下patchKeyedChildren函数的源码:
上面代码包含的有两个while循环和两对if-else;
for (i = s2; i <= e2; i++)
for循环遍历剩余c2,收集每个c2的元素的key,构成map => keyToNewIndexMap;for (i = 0; i < toBePatched; i++)
for循环遍历剩余c2部分长度来生成映射,并赋值为0;for (i = s1; i <= e1; i++)
for循环遍历剩余c1,使用key进行直接获取(for循环剩余c2进行获取)newIndex,此处证明还是要绑定好key,唯一性很重要;newIndex有值说明c2中存在当前老的元素在c1中,老的preChild,在c2中还需要,则调用patch
;如果newIndex为undefined,则说明老的preChild在c2中不需要了,调用unmount,把当前preChild卸载掉;for (i = toBePatched - 1; i >= 0; i--)
;如果(newIndexToOldIndexMap[i] === 0
则证明当前nextChild为新的节点,调用patch;否则判断之前是否发生了移动moved,经过逻辑判断,调用move;patchKeyedChildren 例子
根据咱们上面的例子,由old: ['a', 'b', 'c', 'd']变更为new: ['a', 'd', 'e', 'b']的过程如下:
for (i = s1; i <= e1; i++)
for循环遍历剩余c1阶段,此时i为1,s1为1,s2为1:for (i = toBePatched - 1; i >= 0; i--)
倒着遍历剩余c2阶段,此时i为2,j为0,s1为1,s2为1,newIndexToOldIndexMap为[4, 0, 2]:isSameVNodeType
大家可以看下下面isSameVNodeType的代码,大家在写代码的时候,为了能够提高页面性能,dom diff的速度,如果没有发生变更的元素,key一定要保持一样,不要
v-for="(item, index) in list" :key="index"
这样来写,因为当只有数组内部元素发生了位置移动而元素未发生改变时,index的值是变更的,这样在dom diff的时候就会使程序发生误解。key的唯一性很重要结语
本章主要讲解了Vue3在进行更新操作时所做的事情,着重讲解了在dom diff中的操作,也举了个例子方便大家理解,大家可以对比着Vue2的diff来看,Vue3对dom diff进行了优化,比Vue2性能更高。
The text was updated successfully, but these errors were encountered: