key是什么
在vue中,我们经常使用的指令其中必定有key,我们先看看vue的官网文档中是怎么定义key这个指令的
key
特殊 attribute 主要用做 Vue 的虚拟 DOM 算法的提示,以在比对新旧节点组时辨识 VNodes。如果不使用 key,Vue 会使用一种算法来最小化元素的移动并且尽可能尝试就地修改/复用相同类型元素。而使用 key 时,它会基于 key 的顺序变化重新排列元素,并且 key 不再存在的元素将始终被移除/销毁。有相同父元素的子元素必须有唯一的 key。重复的 key 会造成渲染错误。
- vue使用的虚拟dom,不直接操作dom元素,在操作虚拟dom的时候又使用了diff算法。diff算法的实现基于两个假设: 两个相同的组件产生类似的DOM结构,不同的组件产生不同的DOM结构。
- 同一层级的一组节点,他们可以通过唯一的id进行区分。基于以上这两点假设,使得虚拟DOM的Diff算法的复杂度从O(n^3)降到了O(n)。 粘一段vue比较是否是相同节点的代码:
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)
)
)
)
}
从这里可以看到,如果我们给元素一个唯一的key值,在dom发生变化时可以很快速的判断出是否是同一节点,从而判断是否需要重新渲染dom。
如果没有key,C,D,E节点都需要重新渲染
如果每个组件都有key值,那么他会直接比较原组件里的key值是否存在,只需要插入新增的F节点即可,不需要重新渲染CDE节点
为什么v-for不建议使用index做key
使用v-for
更新已渲染的元素列表时,默认用就地复用
策略;列表数据修改的时候,他会根据key值去判断某个值是否修改,如果修改,则重新渲染这一项,否则复用之前的元素。如果用index做key,下标发生改变时可能就会出现一些问题,比如:
<div>
<p v-for="(v,i) in spans" :key="i">
<input type="text"><button @click="del(i)">删除</button>
</p>
</div>
setup () {
let spans = reactive([])
spans.push({ id: 1, value: 1 }, { id: 2, value: 2 }, { id: 3, value: 3 })
let del = (i) => {
console.log(`删除第${i + 1}行`)
spans.splice(i,1)
}
return { spans, del }
}
明明我们删除的是第二行,可是为什么界面上第三行没有了。
- 删除第二行,数据发生了变化,数据变化驱动视图层变化,spans的长度变为2,那么只剩下了key=0和key=1的p节点。原先存在key=1的节点,vue的复用策略会让继续复用前两个节点,render过后页面上的第三行没有了。
- 有时候我们复现不出来,是因为我们组件的props和data的数据关联起来了,第一次render过程和之前一样,但是props值改变又触发了一次render过程,绑定了正确的props,所以看起来时正常的样子。如图中的代码input输入框没有双向绑定,所以没有触发第二次的render。
- 如果子节点是文本节点的话,vue也做了单独处理,直接替换原先的文本节点,所以也不会复现出上面的情况 所以在我们绑定key值的时候建议使用唯一值,比如后端数据接口返回的列表id,或者前端生产一个uuid
结语
写的不好,有错误的地方烦请各位大佬多多指教,感恩家人感恩🙏