关于Vue组件使用key的思考

155 阅读3分钟

 本文正在参加「金石计划」 

本文需要简单了解diff算法

对于key的使用,相信大多数人都知道,它可以提高组件复用率,在我的日常编码过程中,使用key的两大主要因素为:1.防止Eslint报错;2.强制刷新组件,防止复用

我潜意识一直认为key可以使得渲染加载更加迅速,但事实似乎并非如此

以下为个人的一些认识,如果有错误的地方希望大家帮忙指正

新旧虚拟dom比对源码地址

以下为判断新老虚拟dom节点是否为相同节点的函数

当key不存在时,判断的是标签tag,isComment:注释节点/文本节点

function sameVnode (a, b) {
    return (
        a.key === b.key &&
        a.asyncFactory === b.asyncFactory && (
            (
                a.tag === b.tag &&
                a.isComment === b.isComment &&
                isDef(a.data) === isDef(b.data) && sameInputType(a, b)
             ) || (
                isTrue(a.isAsyncPlaceholder) && isUndef(b.asyncFactory.error)
             )
        )
    )
}

误区1:认为复用组件效率更高

例:当updateChildren指针指向节点比对时,当指针指向A,B两个节点时,假设A,B同标签但是内容不同,B为位置调到前面的节点。

1.无key时,sameVnode判断为true,进入patchVnode继续比对子节点

a.若无children只有innertext直接改变innertext

b.若有children继续下一步比对

2.有key时,saveVnode判断为false,进入前后两指针的以及经过查找发现在老的dom中发现B节点,需要将其位置调到前面

最初以为的操作:位置调动

实际的操作:源码位置

image.png

这里改变了真实的dom

误区2: 在updateChildren中使用了keyMap.has(Key),使用map映射来查找匹配节点,而非遍历,所以速度更快

在标签不同且key值不存在的情况下,确实是遍历要花费更多时间,但是对于一般标签相同的情况,sameVnode判断为true,大部分情况不会进入四个指针匹配不到而遍历查找这种情况。

所以综上所述,使用key的效率并不会更高,而为什么要使用key呢?vue官方给出的说明是这个

预期:number | string | boolean (2.4.2 新增) | symbol (2.5.12 新增)

key 的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。

有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。

最常见的用例是结合 v-for:

image.png

它也可以用于强制替换元素/组件而不是重复使用它。当你遇到如下场景时它可能会很有用:

完整地触发组件的生命周期钩子 触发过渡 例如:

<transition> <span :key="text">{{ text }}</span> </transition> 当 text 发生改变时, 总是会被替换而不是被修改,因此会触发过渡。

推荐大家去看这个issue: 第 1 题:写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?