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

1,025 阅读4分钟

萌新的自我学习总结,大佬们可以多指点。

       我们使用v-for实现列表渲染,绑定key和没有绑定key有什么区别?Vue到底做了什么?

首先什么是虚拟节点?

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

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

(vue3源码\packages\runtimecore\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会基于可以的变化重新排列元素顺序,并且会移除/销毁可以不存在的元素。