详解v-for中:key属性的作用

Posted Dax1_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了详解v-for中:key属性的作用相关的知识,希望对你有一定的参考价值。

举个栗子

不设置key

 <div id="app">
    <div>
      <input type="text" v-model="name">
      <button @click="add">添加</button>
    </div>
    <ul>
      <li v-for="(item, i) in list">
        <input type="checkbox"> item.name
      </li>
    </ul>
<script>
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue(
      el: '#app',
      data: 
        name: '',
        newId: 3,
        list: [
           id: 1, name: '李斯' ,
           id: 2, name: '吕不韦' ,
           id: 3, name: '嬴政' 
        ]
      ,
      methods: 
        add() 
         //注意这里是unshift
          this.list.unshift( id: ++this.newId, name: this.name )
          this.name = ''
        
      
    );
  </script>
  </div>

当选中吕不韦时,添加楠楠后选中的确是李斯,并不是我们想要的结果,我们想要的是当添加楠楠后,一种选中的是吕不韦


设置key

  <div id="app">
    <div>
      <input type="text" v-model="name">
      <button @click="add">添加</button>
    </div>
    <ul>
      <li v-for="(item, i) in list" :key="item.id">
        <input type="checkbox"> item.name
      </li>
    </ul>
<script>
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue(
      el: '#app',
      data: 
        name: '',
        newId: 3,
        list: [
           id: 1, name: '李斯' ,
           id: 2, name: '吕不韦' ,
           id: 3, name: '嬴政' 
        ]
      ,
      methods: 
        add() 
         //注意这里是unshift
          this.list.unshift( id: ++this.newId, name: this.name )
          this.name = ''
        
      
    );
  </script>
  </div>

同样当选中吕不为时,添加楠楠后依旧选中的是吕不为。



可以简单的这样理解:加了key(一定要具有唯一性) id的checkbox跟内容进行了一个关联。是我们想达到的效果


为什么要key

  • vue中列表循环需要加:key='唯一标识',唯一标识尽量是id,目的是为了高效地更新虚拟DOM
  • key主要用于dom diff算法,diff算法为同级比较,比较当前标签上的key还有他当前的标签名,如果key和标签名都一样时只移动,不会重新创建元素和删除元素
  • 没有key地时候默认使用就地复用策略。如果数据的顺序被改变,vue不是移动DOM元素来匹配数据项的改变,而是简单复用原来位置的每个元素,在进行比较时发现标签一样值不一样时,就会复用之前的位置,将新值直接放到该位置,以此类推,最后多出一个就会把最后一个删除掉。
  • 尽量不要使用索引值index作key值,一定要用唯一标识的值,如id等。因为若用数组索引index为key,当向数组中指定位置插入一个新元素后,因为这时候会重新更新index索引,对应着后面的虚拟DOM的key值全部更新了,这个时候还是会做不必要的更新,就像没有加key一样,因此index虽然能够解决key不冲突的问题,但是并不能解决复用的情况。如果是静态数据,用索引号index做key值是没有问题的。

为什么key不要用index

继续举个栗子

const list = [
    
        id: 1,
        name: 'test1',
    ,
    
        id: 2,
        name: 'test2',
    ,
    
        id: 3,
        name: 'test3',
    ,
]
<div v-for="(item, index) in list" :key="index" >item.name</div>

上面的代码使用了index作为key

在列表最后添加一条数据

const list = [
    
        id: 1,
        name: 'test1',
    ,
    
        id: 2,
        name: 'test2',
    ,
    
        id: 3,
        name: 'test3',
    ,
    
        id: 4,
        name: '我是在最后添加的一条数据',
    ,
]

此时前三条数据直接复用之前的,新渲染最后一条数据,此时用index作为key,没有任何问题

在中间插入一条数据

const list = [
    
        id: 1,
        name: 'test1',
    ,
    
        id: 4,
        name: '我是插队的那条数据',
    
    
        id: 2,
        name: 'test2',
    ,
    
        id: 3,
        name: 'test3',
    ,
]

此时更新渲染数据,通过index定义的key去进行前后数据的对比,发现

之前的数据                         之后的数据

key: 0  index: 0 name: test1     key: 0  index: 0 name: test1
key: 1  index: 1 name: test2     key: 1  index: 1 name: 我是插队的那条数据
key: 2  index: 2 name: test3     key: 2  index: 2 name: test2
                                 key: 3  index: 3 name: test3

通过上面清晰的对比,发现除了第一个数据可以复用之前的之外,另外三条数据都需要重新渲染;

最好的办法是使用数组中不会变化的那一项作为key值,对应到项目中,即每条数据都有一个唯一的id,来标识这条数据的唯一性;使用id作为key值,我们再来对比一下向中间插入一条数据,此时会怎么去渲染

之前的数据                               之后的数据

key: 1  id: 1 index: 0 name: test1     key: 1  id: 1 index: 0  name: test1
key: 2  id: 2 index: 1 name: test2     key: 4  id: 4 index: 1  name: 我是插队的那条数据
key: 3  id: 3 index: 2 name: test3     key: 2  id: 2 index: 2  name: test2
                                       key: 3  id: 3 index: 3  name: test3

现在对比发现只有一条数据变化了,就是id为4的那条数据,因此只要新渲染这一条数据就可以了,其他都是就复用之前的;

diff算法图解

diff算法的处理方法,对操作前后的dom树同一层的节点进行对比,一层一层对比


在以下的使用场景:

我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的:


即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?

所以我们需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。

所以一句话,key的作用主要是为了高效的更新虚拟DOM。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。

总结

  • 简单通俗地讲,没有key时,状态默认绑定的是位置,有key时,状态根据key的属性值绑定到了响应的数组元素。
  • key是为了更高效的更新虚拟DOM
  • 推荐使用数组内的字段(保证唯一性)作为key的唯一标识,不建议直接使用index

参考文档

VUE中演示v-for为什么要加key

vue核心面试题:v-for中为什么要用key

Vue方向:v-for循环中的key属性

参考技术A v-for循环中的key属性

使用v-for更新已渲染的元素列表时,默认用就地复用策略,列表数据修改的时候,他会根据key值去判断某个值是否修改,如果修改,则重新渲染这一项,否则复用之前的元素;我们在使用的时候经常会使用index(即数组的下标)来作为key,但其实这是不推荐的一种使用方法;

key值的使用其实是和vue响应式以及虚拟DOM有关。

这样vue就会分析到其他的数据都不需要改变,只需要在新增一个DOM节点,然后添加新增的数据就可以了。

但是,如果输入我们是在数组中间插入的数据就会不一样了。

即后面的节点会不断的更新,这样做就会很没效率。

所以我们需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。

key的作用主要是为了高效的更新虚拟DOM,另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。

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

以上是关于详解v-for中:key属性的作用的主要内容,如果未能解决你的问题,请参考以下文章

列表渲染 之 v-for遍历数组和对象(利用key属性实现高效更新)

Vue中使用v-for渲染数据为何要添加key属性?(原理及作用)

v-for中key的作用

图解vue中 v-for 的 :key 的作用,虚拟dom Diff算法

VUE中的v-for

*vue v-for中 key 值的作用—key不能用index的值*