v-for中经常使用key作为标识,这其中涉及了虚拟DOM与真实DOM、虚拟DOM的对比算法等。
key的取值有时也不可以简单的定为index。

真实DOM与虚拟DOM

虚拟DOM为react与vue两个框架都带来了跨平台的能力。
虚拟DOM实际是一层对真实DOM的抽象。以JS对象(vnode节点)作为基础的树,用对象的属性描述节点,最终通过一系列操作使这棵树映射到真实环境上,也就是真实DOM。
创建虚拟DOM就是为了更好的将虚拟节点渲染到页面视图中。
虚拟DOM对象的节点与真实DOM的属性一一照应。

虚拟DOM节点:Vnodes。
虚拟DOM是在内存中的,真实DOM是用户可以操作的DOM。
虚拟DOM的对比算法(diff算法)就是依赖key完成的

虚拟DOM中key的作用

key是虚拟DOM对象的标识,当状态中的数据发生变化的时候,vue会根据新数据生成新的虚拟DOM。然后vue会进行新虚拟DOM与旧虚拟DOM的比较【diff算法】。

diff算法原理

  • 旧虚拟DOM中找到了与新虚拟DOM相同的key:
    • 如果虚拟DOM中的内容没变,直接使用之前的真实DOM.
    • 如果虚拟DOM中的内容改变,则生成新的真实DOM,并替换页面中之前的旧真实DOM。
  • 旧虚拟DOM中未找到与新虚拟DOM相同的key:
    • 创建新的真实DOM,然后渲染到页面中。

总结来说,diff算法就是通过比较虚拟dom中vnode的key与内容,得出一些需要修改的最小单位,再更新视图。减小了dom的操作,提高了性能

使用index作为key

使用index作为key使用是可能出现问题的。

  • 如果对数据进行逆序添加/删除等破坏顺序操作,会产生没有必要的DOM更新。(页面效果没有问题,但是DOM会有不需要的更新,效率较低)。
  • 如果本身结构中还包括输入类的DOM(比如input输入框):会产生DOM的错误更新,导致页面效果出错。

一个例子:【对数据进行逆序添加】
初始:页面展示一个人员列表,每个人员信息后有一个输入框(使用index作为key进行数组遍历)。

<body>
    <div id="app">
        <h2>人员列表(遍历数组)</h2>
        <ul>
            <li v-for="(p,index) in persons" :key="index">
                {{p.name}}--{{p.age}}
                <input type="text"></input>
            </li>
        </ul>
    </div>
</body>
</html>
<script>
    var vm = new Vue({
        el:'#app',
        data(){
            return{
                persons:[
                    {id:'001',name:'张三',age:18},
                    {id:'002',name:'里斯',age:10},
                    {id:'003',name:'王五',age:20},
                ]
            }
        }
    })
</script>

在这里插入图片描述
大概是这个样子。
然后我们在输入框中写一些内容。为了便于我们辨别不同人员的输入框,在输入框里写每个人员的信息,大概如下:
在这里插入图片描述
然后我们在数组的起始位置(也就是张三的前一位)添加一个新的人员信息(老刘)。

<body>
    <div id="app">
        <h2>人员列表(遍历数组)</h2>
        <ul>
            <li v-for="(p,index) in persons" :key="index">
                {{p.name}}--{{p.age}}
                <input type="text"></input>
            </li>
        </ul>
        <button @click="add">点我添加老刘</button>
    </div>
</body>
</html>
<script>
    var vm = new Vue({
        el:'#app',
        data(){
            return{
                persons:[
                    {id:'001',name:'张三',age:18},
                    {id:'002',name:'里斯',age:10},
                    {id:'003',name:'王五',age:20},
                ]
            }
        },
        methods: {
            add(){
                this.persons.unshift({id:'004',name:'老刘',age:99})
            }
        }
    })
</script>

点击按钮前:
在这里插入图片描述
点击按钮后:
在这里插入图片描述
这里发现在逆序添加数据的时候,原来的信息错位了。其实就是绑定key=index导致的。
然后我们看一下在添加人员信息时,虚拟dom与真实dom发生了怎样的变化:
1>初始数据
在这里插入图片描述
2>初始的虚拟dom(Vnodes)
在这里插入图片描述
3>初始的真实DOM(由虚拟DOM转化得到的真实DOM):
在这里插入图片描述
4>逆序添加数据
在这里插入图片描述
5>由新数据生成的新虚拟DOM:
在这里插入图片描述
这时,重点来了!!! 这里并不是根据新虚拟DOM构建新真实DOM,而是通过diff算法(让旧虚拟DOM 与 新虚拟DOM 进行比较,找出不同的地方再进行更新)。
在这里插入图片描述
可以看到新虚拟DOM的第一项是key为0,然后diff算法会到旧虚拟DOM中寻找是否存在key=0的节点,刚好找到了旧虚拟DOM中的第一行(张三18的数据),然后依次对比这一行的数据,发现只有里面的文字改变了(由张三18改为老刘30),input标签并没有改变。
然后新真实DOM会将没有改变的input从旧真实DOM中直接复用,而改变了的文字数据(老刘30)重新生成。
以此类推,造成了数据的错位。也就是下图这样:
在这里插入图片描述

使用id作为key

数据不会错乱,而且效率会更高。
上述例子中,如果将循环绑定的key值改为p.id

<li v-for="(p,index) in persons" :key="p.id">
     {{p.name}}--{{p.age}}
     <input type="text"></input>
 </li>

就不会引起数据错乱。这时diff算法的工作流程:
新虚拟DOM的第一项是key为004,然后diff算法会到旧虚拟DOM中寻找是否存在key=004的节点,没有找到。将这一项直接生成新的真实DOM。
在这里插入图片描述
然后到了新虚拟DOM中的第二行(key=001),寻找旧虚拟DOM中是否有key=001的节点,刚好找到了旧虚拟DOM中的第一行(张三18的数据),然后依次对比这一行的数据,发现里面的文字内容和input标签都没有改变,直接将旧真实DOM中的这条复用过来。
以此类推,最终得到新的真实DOM,并且数据没有错位,效率也更高。
在这里插入图片描述

v-for遍历时不写key

vue默认将index索引值作为key进行遍历。

总结

1、开发中最好使用数据的唯一标识作为key,比如id、手机号、身份证号、学号等。
2、如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,只用于渲染列表进行展示,使用index作为key也是没有问题的。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐