v-for时为什么要写 key ?

前言

官方文档中写到 「建议尽可能在使用 v-for 时提供 key attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。」

开发vue的朋友也都知道,一定要去写上key 不然通过vue-cli创建的项目会报出警告。也知道key是用来标记当前元素。这篇文章的目的是看一下源码,了解下我们为啥要去写这个key

虚拟dom回忆

之前写过关于虚拟dom执行的流程。我们了解到

  1. 如果oldVnode不是dom元素,并且和vnodesameVnode会进行patchVnode
  2. pathVnode中,如果新老节点都有子节点并且不相等,会执行updateChildren对比子节点,把子节点的差异更新到真实dom
  3. sameVnode中会对比两个虚拟dom的keytag

从代码体会key的作用

下面的代码是没有加key的情况,在页面遍历展示arr数组,当点击按钮的时候往数组插入’X’。

<div id="app">
<button @click="handler">按钮button>
<ul>
<li v-for="value in arr">{
    {value}}li>
ul>
div>
<script src="../../dist/vue.js">script> <script>
  const vm = new Vue({
      
    el: '#app',
    data: {
      
      arr: ['a', 'b', 'c', 'd']
    },
    methods: {
      
      handler () {
      
		this.arr.splice(1,0,'X')
	 }
   } 
 })
script>

下面点击按钮后会经历些什么

  1. 点击按钮后,oldVnode是: ul, vnodeul, 符合sameVnode。进行patchVnode
  2. oldVnode的子节点遍历内容是:['a', 'b', 'c', 'd'], vnode的子节点遍历内容是:['a', 'X' ,'b', 'c', 'd'], 新老节点都有子节点并且不相等。进入updateChildren对比子节点
  3. updateChildren 是diff算法的核心,我在文章的最后写了他的对比过程

总结

  • 当没有设置 key 的时候
    updateChildren 中比较子节点的时候,会做三次更新 DOM 操作和一次插入 DOM 的操作
  • 当设置 key 的时候
    updateChildren 中比较子节点的时候,因为 oldVnode 的子节点的 b,c,dnewVnodex,b,ckey 相同,所以只做比较,没有更新 DOM 的操作,当遍历完毕后,会再把 x 插入到 DOM 上DOM 操 作只有一次插入操作。

—————————————————————————————————华丽的分割线

updatechildren的对比过程

  • 对比开始和结束节点的时候分四种
    v-for时为什么要写 key ?_第1张图片

  • 旧开始/新开始

    如果旧开始和新开始是sameVnode(key和sel相同)

    • 调用patchVnode()对比和更新节点
    • 把旧开始和新开始索引往后移动 oldStartIdx++ / oldEndIdx++
      如果不是sameVnode, 开始 旧结束/新结束比较
  • 旧结束/新结束

    如果旧结束和新结束是sameVnode(key和sel相同)

    • 调用patchVnode()对比和更新节点
    • 把旧开始和新开始索引往后移动 oldStartIdx-- / oldEndIdx–
      如果不是, 开始 旧开始/新结束比较
  • 旧开始/新结束

    如果旧开始和新结束是sameVnode(key和sel相同)

    • 调用patchVnode()对比和更新节点

    • 把oldStartVnode 对应的dom元素,移动到右边,更新索引
      v-for时为什么要写 key ?_第2张图片

      如果不是, 开始 旧结束/新开始比较

  • 旧结束/新开始

    如果是sameVnode

    • 调用patchVnode()对比和更新节点
    • 把oldEndVnode 对应的dom元素,移动到左边,更新索引
      v-for时为什么要写 key ?_第3张图片
  • 如果以上四种都不满足

  1. 首先遍历新开始节点,在旧节点数组中依次查找是否有相同key值的节点,
  2. 如果没有,创建新dom元素插入到最前面的位置
  3. 如果找到了,并且判断sel属性是否相同,如果相同,旧节点会被复制给elmToMove,然后调用patchVNode对比节点,然后把elmToMove对应的dom节点移动到最前面
    ——————循环结束后
  • 当老节点的所有子节点先遍历完成,说明新节点有剩余,把剩余节点批量插入到右边
  • 新节点的所有子节点先遍历完成,说明老节点有剩余,把剩余老节点删除

你可能感兴趣的:(Vue.js源码分析,vue,vue.js)