Vue3 v-for中无key和有key实现的简单解读

首先什么是虚拟节点?

        一般来说HTML的元素解析成DOM树后,真实挂载的元素就是Node。而VNode(虚拟节点)(本质是一个JS对象)是Vue解析template里面的元素生成的,而这些VNode组成就会形成一个VNodeTree(虚拟DOM),而虚拟DOM再经过一些操作才会变成真实的DOM(不一定是一一对应的)。所以VNode可以做多平台的渲染。


那么如果使用v-for列表渲染一个数组,那么有个添加操作,在数组中间插入一个元素,那么插入渲染怎么才可以性能更好?

(vue3源码\vue-next-3.0.11\packages\runtime-core\src\renderer.ts里有两个方法的实现)

没有key就会调用patchUnKeyedChildren方法

        Vue源码会有c1保存着旧的VNode,c2保存着新的VNode,Vue内部就会先获取旧的和新的VNode数组(列表)的长度,再Math.min(c1.length,c2.length)获取新旧数组中长度最短的值,然后遍历短的VNode列表(为什么会判短的,因为要避免越界的情况),分别获取c2和c1里面的一个值,下一步就进行新旧VNode的patch(更新),相同的就不更新,不同的就更新(如果类型相同,但是里面的内容不同,就只更新里面的内容就可以),直到遍历完。因为用的是短的遍历,如果旧的VNode数大于新的VNode数,多出来的旧VNode就会被卸载(unmountChildren)掉,如果旧的VNode数小于新的VNode数,就会创建新的VNode, 然后挂载(mountChildren)到新的VNode列表里,形成新的虚拟DOM后,最后渲染到真实的DOM(而这种处理方法的性能低)。

官方:Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改.复用相同类型元素。

本萌新理解:就是相同的VNode尽可能的复用,不同就复用VNode节点,再修改内容。


key属性主要用在Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes。

        如果使用key,就会使用patchUnkeyedChildren方法,这时vue就采用了diff算法。当执行元素插进去数组中,vue会新生成一个新的VNodes并和原本的VNodes进行比较。(开发中,一般绑定key就是其代表内容的一致性)

        Vue考虑到中间插入删除操作,就会先进行前面和后面的VNode比较,然后在中间就插入新的VNode,也考虑到随便在中间删了一个VNode。Vue内部用一个while循环(因为Vue不知道要在哪儿结束,VNode数量的不确定性)

        第一步,从头部开始遍历,找到新旧VNode数组(列表)里的第一个VNode,然后判断它们的type和key是否一样,如果VNode相同,就会进行patch,然后继续循环遍历,直到查找到不是新旧VNode就不同,才会跳出循环。

        第二步,开始从尾部开始进行遍历,判断它们的VNode是否相同,相同就patch,不同就直接跳出循环。

        第三步,如果旧VNode数组遍历完,新VNode数组还有VNode(新的VNode),就会找到该VNode的位置,然后patch一个null(表示一次挂载操作),新VNode列表就会在该位置新增一个VNode。

        第四步,如果新VNode数组遍历完,旧VNode数组里还有VNode,就找到对应位置的Vnode,直接卸载掉。

        第五步,如果前后都比较完后,新旧VNode数组中间部分的VNode数相等(或不等)并且无序,Vue会新建一个数组arr,会尽可能在旧VNode数组里找到对应新VNode数组的VNode,其实就是根据key建立索引找到它们相同的VNode,然后把旧VNode数组里含有的这个VNode放到arr里,直到相同的旧Vnode都patch完放到arr后,而这时如果旧VNode数组里还有多出来的VNode就会被卸载掉,而新VNode数组里还有多出来的VNode就会被放入arr里,最后再把arr插入到新的VNode中间。

官方:Vue会基于可以的变化重新排列元素顺序,并且会移除/销毁可以不存在的元素。

你可能感兴趣的:(Vue3 v-for中无key和有key实现的简单解读)