我们在使用v-for时,需要给单元上加上key
<ul>
<li v-for="item in items" :key="item.id">...</li>
</ul>
key是每个vnode的唯一id,也是diff的一种优化策略,可以根据key,更快的找到对应的节点,更高效的更新vnode,避免频繁更新不同元素,减少dom操作,提高性能
如上图,插入E,通过对比分析使用key与不适用key的区别。
使用key
每次循环新老树对比,将一样的元素放入相应的位置
A B C D
A B E C D
- 1次循环,比较A,相同节点,patchA,不发生dom操作
B C D
B E C D
- 2次循环,比较B,相同节点,patchB,不发生dom操作
C D
E C D
- 3次循环,C, E不同节点,比较D,相同节点,patchD,不发生dom操作
C
E C
- 4次循环,比较C,相同节点,patchC,不发生dom操作
oldCh 全部处理结束,newCh 中只剩下 E,创建 E 并插入到 C 前面
结论:共进行了4次 patch,1次追加新dom的操作
不使用key
通过vue源码来看,未设置key,key=undefined,undefined 是恒等于 undefined,会人为这是相同节点,新旧vnode 进行diff,然后将对比出来的结果更新真是dom
A B C D
A B E C D
- 1次循环,比较A,相同节点,patchA,不发生dom操作
B C D
B E C D
- 2次循环,比较B,相同节点,patchB,不发生dom操作
C D
E C D
- 3次循环,比较C、E,相同节点,进行 patch,数据不同,发生dom操作
D
C D
- 4次循环,比较D、C,相同节点,进行 patch,数据不同,发生dom操作
oldCh 全部处理结束,newCh 中剩下 D,创建 D 并插入 dom 中
结论:共进行了4次 patch,2次更新,1次追加新dom的操作
设置key值一定能提高diff效率吗?
答案:不一定
这种模式默认高效,但是只适用于不依赖子组件状态或临时 DOM 状态
建议尽可能在使用 v-for 时提供 key,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升
不能用index做key
影响性能:当用index作为key的时候,删除节点后面的所有节点都会导致重新渲染,因为index变化了,可以也就变化了
结论
1、key的作用主要是为了高效更新vnode。其原理:vue在patch过程中通过key可以精准判断两个节点是不是同一个节点,从而避免频繁更新不同的元素,使得整个pathch 更加高效,减少dom操作,提高性能
2、另外,若不设置key还可能在列表更新时候引发一些隐藏的bug。
3、vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。
vue 中有关于 key
key
在vue中,列表渲染的时候需要我们定义key。那么key在vue中有什么作用呢。
key主要作用于Vue的virtual DOM算法,在diff new nodes list和old nodes list时,作为识别VNode的一个线索
那么知道key是用于 dom diff算法中使用的,也就是说当key发生改变的时候会触发组件重新渲染更新
使用key的改变,使animate动画重新触发
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.0/animate.min.css"
/>
<style>
#box {
width: 100px;
height: 100px;
background-color: blanchedalmond;
margin: 100px;
}
</style>
</head>
<body>
<div id="app1"></div>
<div id="app2"></div>
<script>
const app1 = new Vue({
template: '<div id="box" :class="className" @click="reanimate"></div>',
data() {
return {
className: "animate__animated animate__bounce",
};
},
methods: {
reanimate() {
var _this = this;
this.className = "";
setTimeout(() => {
_this.className = "animate__animated animate__bounce";
});
},
},
});
const app2 = new Vue({
template:
'<div id="box" :class="className" :key="key" @click="reanimate"></div>',
data() {
return {
className: "animate__animated animate__bounce",
key: 1,
};
},
methods: {
reanimate() {
++this.key;
},
},
});
app1.$mount("#app1");
app2.$mount("#app2");
</script>
</body>
</html>