性能消耗
使用 index 做 key,破坏顺序操作的时候, 因为每一个节点都找不到对应的 key,导致部分节点不能复用,所有的新 vnode 都需要重新创建。
例子:
- {{item.name}}
export default {
name: 'HelloWorld',
data() {
return {
studentList: [
{ id: 1, name: '张三', age: 18 },
{ id: 2, name: '李四', age: 19 },
],
};
},
methods:{
addStudent(){
const studentObj = { id: 3, name: '王五', age: 20 };
this.studentList=[studentObj,...this.studentList]
}
}
}
我们先把 Chorme 调试器打开,我们双击把里面文本修改一下
我们运行以上上面的代码,看下运行结果
点击前
点击后
从上面运行结果可以看出来,我们只是添加了一条数据,但是三条数据都需要重新渲染是不是很惊奇,我明明只是插入了一条数据,怎么三条数据都要重新渲染?而我想要的只是新增的那一条数据新渲染出来就行了。
上面我们也讲过 diif 比较方式,下面根据 diff 比较绘制一张图,看看具体是怎么比较的吧
当我们在前面加了一条数据时 index 顺序就会被打断,导致新节点 key 全部都改变了,所以导致我们页面上的数据都被重新渲染了。
下面我们下面生成1000个 DOM 来比较一下采用 index ,和不采用 index 性能比较,为了保证 key 的唯一性我们采用 uuid 作为 key
我们用 index 做为 key 现执行一遍
- {{item.id}}
import uuidv1 from 'uuid/v1'
export default {
name: 'HelloWorld',
data() {
return {
studentList: [{id:uuidv1()}],
};
},
created(){
for (let i = 0; i < 1000; i++) {
this.studentList.push({
id: uuidv1(),
});
}
},
beforeUpdate(){
console.time('for');
},
updated(){
console.timeEnd('for')//for: 75.259033203125 ms
},
methods:{
addStudent(){
const studentObj = { id: uuidv1() };
this.studentList=[studentObj,...this.studentList]
}
}
}
换成 id 作为 key
- {{item.id}}
beforeUpdate(){
console.time('for');
},
updated(){
console.timeEnd('for')//for: 42.200927734375 ms
},
从上面比较可以看出,用唯一值作为 key 可以节约开销
数据错位
上述例子可能觉得用 index 做 key 只是影响页面加载的效率,认为少量的数据影响不大,那面下面这种情况,可能用 index 就可能出现一些意想不到的问题了,还是上面的场景,这时我先再每个文本内容后面加一个 input 输入框,并且手动在输入框内填写一些内容,然后通过 button 向前追加一位同学看看
- {{item.name}}
export default {
name: 'HelloWorld',
data() {
return {
studentList: [
{ id: 1, name: '张三', age: 18 },
{ id: 2, name: '李四', age: 19 },
],
};
},
methods:{
addStudent(){
const studentObj = { id: 3, name: '王五', age: 20 };
this.studentList=[studentObj,...this.studentList]
}
}
}
我们往 input 里面输入一些值,添加一位同学看下效果:
点击前
点击后
这时候我们就会发现,在添加之前输入的数据错位了。添加之后王五的输入框残留着张三的信息,这很显然不是我们想要的结果。
从上面比对可以看出来这时因为采用 index 作为 key 时,当在比较时,发现虽然文本值变了,但是当继续向下比较时发现 DOM 节点还是和原来一摸一样,就复用了,但是没想到 input 输入框残留输入的值,这时候就会出现输入的值出现错位的情况
解决方案
既然知道用 index 在某些情况下带来很不好的影响,那平时我们在开发当中怎么去解决这种情况呢?其实只要保证 key 唯一不变就行,一般在开发中用的比较多就是下面三种情况。
在开发中最好每条数据使用唯一标识固定的数据作为 key,比如后台返回的 ID,手机号,身份证号等唯一值可以采用 Symbol 作为 key,Symbol 是 ES6 引入了一种新的原始数据类型 Symbol ,表示独一无二的值,最大的用法是用来定义对象的唯一属性名。
let a=Symbol('测试')
let b=Symbol('测试')
console.log(a===b)//false
可以采用 uuid 作为 key ,uuid 是 Universally Unique Identifier 的缩写,它是在一定的范围内(从特定的名字空间到全球)唯一的机器生成的标识符
我们采用上面第一种方案作为 key 在看一下上面情况,如图所示。key 相同的节点都做到了复用。起到了diff 算法的真正作用。
点击前
点击后
总结
用 index 作为 key 时,在对数据进行,逆序添加,逆序删除等破坏顺序的操作时,会产生没必要的真实 DOM更新,从而导致效率低用 index 作为 key 时,如果结构中包含输入类的 DOM,会产生错误的 DOM 更新在开发中最好每条数据使用唯一标识固定的数据作为 key,比如后台返回的 ID,手机号,身份证号等唯一值如果不存在对数据逆序添加,逆序删除等破坏顺序的操作时,仅用于渲染展示用时,使用 index 作为 key
也是可以的(但是还是不建议使用,养成良好开发习惯)。