vue中key的作用

摘要

本文结合源码说明了key的作用,给出了一个不写key的反例,并分析了原因。

内容

  • key的作用

  • 不写key会出的bug及原因分析

  • key的实践方案

key的作用

在数据变化前后,vue会得到两个虚拟dom树,并依次比较两个虚拟dom树中哪些结点有变动,从而决定去做具体的更新。这里有一个操作要做:比较两个结点是不是同一结点,这个操作在源码是通过sameVnode来完成的。见如下代码。

 // https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js
 function sameVnode (a, b) {
   return (
     a.key === b.key && (
      (
         a.tag === b.tag &&
         a.isComment === b.isComment &&
         isDef(a.data) === isDef(b.data) &&
         sameInputType(a, b)
      ) || (
         isTrue(a.isAsyncPlaceholder) &&
         a.asyncFactory === b.asyncFactory &&
         isUndef(b.asyncFactory.error)
      )
    )
  )
 }

假设我们给一组元素中的每一项都设置了一个唯一的标识,则a.key===b.key就可以更快地返回结果,这就是提升性能的关键点。

在实践中,渲染一组列表时,key往往是唯一标识(可能其它的条件是相同的),所以,如果不定义key,vue只能认为比较的两个节点是同一个 (undefined===undefined),哪怕它们实际上不是,这导致了频繁更新元素,使得整个patch过程比较低效,影响性能。有些特殊的时候甚至会有一些bug。

bug来了

下面这一段代码是硬编出来的,用它来说明如果在v-for循环中不写key,或者key没有正确使用会带来的后果。

 
 <div id="app" style="padding:20px">
   <coma v-for="(it,idx) in list" :i="idx" @click="d">{
  
  {it}}</coma>
 </div>
 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script>
 <script>
   Vue.component('coma',{
     data() {
       return {
         v: Math.random()
      }
    },
     template: `
 <div @click="$emit('click',i)">
 <slot></slot>-<span>{
  
  {v}}</span> <button>点我删除</button>
   </div>
 `,
     props: ['i']
  })
   new Vue({
     el: "#app",
     data: {
       list: [1, 2,3,4,5]
    },
     methods: {
       d(idx) {
         this.list.splice(idx, 1)
      }
    }
  })
 </script>

bug描述:当在某一项上点击删除时,它会删除最后一项,而不是删除当前项(请把注意力放在最后一项)。

图片

原因分析

vue是通过虚拟dom来表示描述真实dom的,在去更新视图之前,要对前后两个虚拟dom树进行分析,以得出它们的区别。如果不设置key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。看官网 https://cn.vuejs.org/v2/api/#key

分析一下上面bug, 以在第2项上点击为例。

期待的情况,在点击前的数据:

 1,2,3

在点击之后的数据

 1,3 // 第二项被删除了。

但是由于没有设置key,此间产生的变化是:

  1. 把原来的第2项中的2改成3

  2. 把第3项删除掉。

所以会导致序号异常,且最后一项被删除了。

还有一个bug可以看这里https://codesandbox.io/s/vue-v-for-key-50x89。类似的情况还会发生在使用了v-transition时。

推荐设置key

可以通过如下的改进来避免上面的坑:

  1. 给列表中的数据项添加唯一的id

 data: {
    list: [{id:1,value:1},{id:2,value:3},{id:3,value:3}]
 },
  1. 修改视图

 <coma v-for="(it,idx) in list" :key="it.id" :i="idx" @click="d">{
  
  {it.value}}</coma>

这样,在删除第二项时,vue会做出正确的判断:删除第二个组件。

小结

  • key的作用主要是为了更高效的对比虚拟dom中的某个节点是不是相同节点,是用来提高diff算法的性能表现。更具体一点,vue在patch过程(执行diff的算法,可翻译为打补丁算法)中判断两个节点是否是相同节点,key值相同是一个必要条件。

  • key会提升效率。某些特殊情况下,不写key会出错。

  • 在使用v-for循环时,尽量避免直接使用数组的下标为key,因为它们在做删除操作时可能会导致渲染异常。

  • 最好是将key设成数据项中的主键:可以把一项与另一项区别开的值。

猜你喜欢

转载自blog.csdn.net/sqLeiQ/article/details/112472865