vue中属性key的作用(了解diff),为什么不建议index作为key

1. 官方文档有关key的说明

key 的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。
而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。

2. 举个🌰

可以运行一下代码,改变key为 索引idx(或者不设置key) 或者 item.id, 看下效果

  • 当索引idx,作为key的时候(或者不设置key),选中一项,再点击添加新值的时候,选中的项会改变
  • 当item.id作为key的时候,选中一项,再添加的时候,选中项不会变化
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

</head>

<body>
    <div id="app">
        <div>
            <input type="text" v-model="name">
            <button @click="add">添加</button>
        </div>
        <ul>
            <!-- <li v-for="(item,idx) in list" :key="idx"> -->
                <li v-for="item in list" :key="item.id">
                <input type="checkbox">ID:{{item.id}} -------name:{{item.name}}
            </li>
        </ul>
    </div>
    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                name: '',
                list: [{
                        id: 0,
                        name: '测试数据1'
                    },
                    {
                        id: 1,
                        name: '测试数据2'
                    },
                    {
                        id: 2,
                        name: '测试数据3'
                    }
                ]
            },
            methods: {
                add() {
                    const newUser = {
                        id: this.list.length,
                        name: this.name
                    };
                    this.list.unshift(newUser);
                }
            }
        })
    </script>
</body>

</html>

可以看出,当key值为item.id的时候,checkbox选中项在添加新项的时候不会变化。为什么会这样呢?我们往下看

3. diff

在vue中,当数据发生变化,会触发更新,更新过程的核心就是新旧 vnode的diff。
diff的原理就是对前后的节点树同一层的节点进行对比,一层一层对比,如下
在这里插入图片描述
当某一层有很多相同的节点时,也就是列表节点时,diff算法的更新过程默认情况下也是遵循以上原则。
如下
在这里插入图片描述

我们希望可以在B和C之间加一个F,diff算法默认执行起来是这样的在这里插入图片描述
即把C更新成F,D更新成C,E更新成D,最后再插入E,相当没有效率!
所以我们需要使用key来给每个节点做一个唯一标识,diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。如下图
在这里插入图片描述

在vue中,判断新旧节点的sameVnode(oldVnode, vnode)函数如下

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)))
  )
}

可以看到,判断两个节点是否一样,首先判断了节点的key
因此,唯一的key值,会在diff比较新旧节点是否相同时,起到关键作用,起到更高效的更新虚拟节点的作用

4. 为什么不推荐索引index作为key

可以参考例子中的代码,在增删变化的场景中,元素的index可能是会变化的,diff算法时比较同级之间的不同,以key来进行关联,当对数组进行下标的变换时,比如删除第一条数据,那么以后所有的index都会发生改变,那么key自然也跟着全部发生改变,所以index作为key值是不稳定的,而这种不稳定性有可能导致性能的浪费,导致diff无法关联起上一次一样的数据。因此,能不使用index作为key就不使用index

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值