在上一章讲到 v-for 列表渲染的博文中,只是说到了 v-for 的基本使用,并没有对其内部进行更深层次的 解读,其实在 v-for 这个指令下,有一个及其关键的 连带属性 key。这个 key 才是 Vue 或者是是 MVVM 框架的 最精髓的东西。
key的使用方式:以数组为例
-
{{person.name}}
和之前相比,发现有什么不同了么?没错,就是在循环标签体中加上了 :key='index' ,正是这个玩意,完成了 MVVM 框架在改变数据之后的高效渲染。
首先,这个key前面有一个冒号,代表的是使用 v-bind 这个指令动态绑定的。
然后这个key绑定的值,需要是唯一的,就是你遍历之后,每个li节点上的key都是不同的,在这个简单例子中,我们可以直接使用 index 来作为 key 的绑定值。
-
{{person.name}}
输出之后能看到在DOM节点上,key这个属性是不存在的,因为这个属性是给Vue使用的,所以不会暴露出来,如果你自己写个自定义属性,例如:a='1' 那么还是会展示出来的。
-
{{person.name}}
如果你说你要是不动态绑定,你就要写死,就要直接 key='1',那我也没话说,直接在控制台上见真章吧。
-
{{person.name}}
根据谷歌翻译,这个错误是:检测到重复键:“1”。 这可能会导致更新错误。
这是个啥子意思呢?重复键又是个啥玩意?怎么就导致更新错误了?杠精直接发起了夺命三连问。
在 解决这些问题之前,我们先来说一下,使用 index 作为 key 的绑定值的弊端在哪。然后再来了解 这个 key 到底是用来干什么的,这样的话,上面的问题就迎刃而解了。
index的弊端:性能开销大,数据对应错误(破坏数据原有顺序)
如果单纯的是上面的展示信息,代码是没有问题的,但是,如果我想在数据中添加一条数据的话,就会出现状况了。
首先,向数组中添加数据的话,有两种情况,第一种是加到最后,第二种是加到最前。
加到最后是正常的,加到最前就会有比较诡异的情况了
性能开销大 这个 问题暂时不太好演示,留下下文搞个大点的例子再来说。先说数据对应错误这个东西。
步骤1、在 li 节点里面添加一个 input 框
步骤2、然后在 input 框里面输入数据
步骤3、点击按钮之后,添加一个新的 person 对象到 数组里面去,重新渲染之后再来看页面展示 ,这里就要分为两种情况了,
第一种,push到数组末尾,这种情况展示下来是没有问题的,因为添加到数组末尾,是没有打乱整个数组排序的,所以展示没有错乱
add() {
const a = { id: 3, name: '赵六' }
this.personArr.push(a)
}
第二种,unshift到数组首位,这样问题就来了,因为打乱了原有排序,v-for的时候,根据key 来处理数据循环渲染时,就会出现这种问题
这个时候就能看到,数据对应错误了,这就是使用 index 作为 关键字 key 的绑定值的弊端。
虚拟DOM中key的作用
首先,key 的作用是用来作为 diff 算法的唯一比对标识的。通过 diff 算法对比 新旧虚拟DOM,然后根据对比结果开始渲染页面。
简单描述一下 diff 算法的对比过程。就拿上面的 index 作为key值 的例子来说。
步骤1:通过初始数据,生成 【初始虚拟DOM】,然后直接渲染成【真实DOM】展示在页面上。
步骤2:改变数据之后,在数组最前添加一条数据,打乱了数据原有排序,且此时仍旧使用index 作为 key 的值。此时,根据【新数据】,生成了【新的虚拟DOM】节点。然后将【新的虚拟DOM】节点与【初始虚拟DOM节点】对比。
步骤三:
(1)、首先在新的虚拟DOM节点列表取出第一条虚拟DOM,和旧的虚拟DOM节点列表对比。先根据 key 来对比,发现 两条数据的 key 都是 0,然后接着对比内部元素,发现 文本节点(赵六-张三)不匹配,所以生成新的真实文本DOM节点。
(2)、接着对比 标签节点(input),因为是虚拟DOM,所以 只比对标签,不比对标签内部value值,发现标签节点是一致的,所以 真实的input DOM节点 复用了。真实input节点复用了,那么input内部的value值也复用了,所以造成了节点数据展示错位。
(3)、如果最开始就发现 key 值 不一样,那么这条虚拟DOM 则不用进行比对了,直接根据新的虚拟DOM生成新的真实DOM,不存在复用的情况了。以此对比,一条条虚拟DOM对比过后,就生成了 新的真实DOM。
步骤四:diff 对比完成之后,能复用的DOM节点就直接复用了,不能复用的,生成新的真实DOM节点,然后渲染到页面上。
ps1:如果是使用数据中的 唯一标识来指定 key 值,那么对比之后,会发现只有新生成的虚拟DOM节点,需要生成新的真实节点,其余的虚拟DOM节点,则是完全相同,可以直接复用,极大的提高了渲染效率
ps2:如果 在遍历的时候,不写key,那么Vue在遍历的时候,默认会把 index 作为 key 的绑定值。
本章小结
首先,key 是只存在于虚拟DOM中的。 是提供给Vue内部使用的,真实DOM节点上是不存在这个属性的。
虚拟DOM中key的作用:这个key绑定的值就相当于是这个节点的 唯一编码,就像我们的身份证号一样,是唯一识别码。当数据发生变化时,Vue 会根据 【新数据】生成 【新的虚拟DOM节点】,随后,根据 diff 算法,对比【旧的虚拟DOM】和 【新的虚拟DOM】,生成真实DOM节点。
diff 对比规则:
1、【旧虚拟DOM】中找到了与【新虚拟DOM】 相同的 key
(1)、若是虚拟DOM中的内容没变,则直接复用之前的真实DOM节点
(2)、若是虚拟DOM中的内容改变,则生成新的真实DOM节点,然后替换之前的真实DOM节点
2、 【旧虚拟DOM】中没有找到与【新虚拟DOM】 相同的 key
(1)、直接创建真实DOM,并且渲染到页面上
3、使用 index 作为 key值,可能会引发的问题
(1)、若是对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新===> 界面效果展示没问题,但是渲染效率低下
(2)、若是结构中还包含 输入类 DOM ( form 表单类元素):则会产生错误的DOM更新 ===> 导致界面渲染错误
4、开发阶段如何选择 key 值?
(1)、最好使用每条数据的唯一标识作为key,例如:id、身份证号码等等
(2)、如果对不存在的数据进行逆序添加,逆序删除等破坏顺序的操作,如果仅仅用于展示,那么 使用 index 作为 key 值是没有问题的。