阅读 803
vue中的key到底有什么用?

vue中的key到底有什么用?

key是什么

在vue中,我们经常使用的指令其中必定有key,我们先看看vue的官网文档中是怎么定义key这个指令的

key 特殊 attribute 主要用做 Vue 的虚拟 DOM 算法的提示,以在比对新旧节点组时辨识 VNodes。如果不使用 key,Vue 会使用一种算法来最小化元素的移动并且尽可能尝试就地修改/复用相同类型元素。而使用 key 时,它会基于 key 的顺序变化重新排列元素,并且 key 不再存在的元素将始终被移除/销毁。有相同父元素的子元素必须有唯一的 key。重复的 key 会造成渲染错误。

  1. vue使用的虚拟dom,不直接操作dom元素,在操作虚拟dom的时候又使用了diff算法。diff算法的实现基于两个假设: 两个相同的组件产生类似的DOM结构,不同的组件产生不同的DOM结构。
  2. 同一层级的一组节点,他们可以通过唯一的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节点都需要重新渲染 image.png 如果每个组件都有key值,那么他会直接比较原组件里的key值是否存在,只需要插入新增的F节点即可,不需要重新渲染CDE节点 image.png

为什么v-for不建议使用index做key

使用v-for更新已渲染的元素列表时,默认用就地复用策略;列表数据修改的时候,他会根据key值去判断某个值是否修改,如果修改,则重新渲染这一项,否则复用之前的元素。如果用index做key,下标发生改变时可能就会出现一些问题,比如:

image.png

image.png

  <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

结语

写的不好,有错误的地方烦请各位大佬多多指教,感恩家人感恩🙏

文章分类
前端
文章标签