文章目录
一、key的作用
在数据变化前后,vue会得到两个虚拟dom树,并依次比较两个虚拟dom树中哪些结点有变动,从而决定去做具体的更新。这里有一个操作要做:比较两个结点是不是同一结点,这个操作在源码是通过sameVnode来完成的。见如下代码。
// https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js
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)
)
)
)
}
假设我们给一组元素中的每一项都设置了一个唯一的标识,则a.key===b.key就可以更快地返回结果,这就是提升性能的关键点。
在实践中,渲染一组列表时,key往往是唯一标识(可能其它的条件是相同的),所以,如果不定义key,vue只能认为比较的两个节点是同一个 (undefined===undefined),哪怕它们实际上不是,这导致了频繁更新元素,使得整个patch过程比较低效,影响性能。有些特殊的时候甚至会有一些bug
下面这一段代码是硬编出来的,用它来说明如果在v-for循环中不写key,或者key没有正确使用会带来的后果。
<div id="app" style="padding:20px">
<coma v-for="(it,idx) in list" :i="idx" @click="d">{{it}}</coma>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script>
<script>
Vue.component('coma',{
data() {
return {
v: Math.random()
}
},
template: `
<div @click="$emit('click',i)">
<slot></slot>-<span>{{v}}</span> <button>点我删除</button>
</div>
`,
props: ['i']
})
new Vue({
el: "#app",
data: {
list: [1, 2,3,4,5]
},
methods: {
d(idx) {
this.list.splice(idx, 1)
}
}
})
</script>
bug描述:当在某一项上点击删除时,它会删除最后一项,而不是删除当前项(请把注意力放在最后一项)。
二、原因分析
1.vue是通过虚拟dom来表示描述真实dom的,在去更新视图之前,要对前后两个虚拟dom树进行分析,以得出它们的区别。如果不设置key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。看官网 https://cn.vuejs.org/v2/api/#key
2.推荐设置key
<coma v-for="(it,idx) in list" :key="it.id" :i="idx" @click="d">{{it.value}}</coma>
这样,在删除第二项时,vue会做出正确的判断:删除第二个组件。
总结
key的作用主要是为了更高效的对比虚拟dom中的某个节点是不是相同节点,是用来提高diff算法的性能表现。更具体一点,vue在patch过程(执行diff的算法,可翻译为打补丁算法)中判断两个节点是否是相同节点,key值相同是一个必要条件。
key会提升效率。某些特殊情况下,不写key会出错。
在使用v-for循环时,尽量避免直接使用数组的下标为key,因为它们在做删除操作时可能会导致渲染异常。
最好是将key设成数据项中的主键:可以把一项与另一项区别开的值。
原版请看微信公众号《凡人进阶》