从前篇文章说起
前几天我写了一篇文章,sortable.js——Vue 数据更新问题 ,当时自己只是数据的强制刷新角度去分析,而且并没找到真正的“元凶”。
很感谢有人帮我指出,可能是 Vue 的 key 值,导致数据渲染不正确的。由此,我做了进一步的尝试。
key 的一个错误使用——使用 index 作为 key
不知道你在写 v-for 的时候,会不会直接使用 index 作为它的 key 值,是的,我承认我会,不得不说,这真的不是一个好习惯。
根据上篇文章,我们还是用 sortable.js 作为例子讨论。以下是核心代码,其中 arrData 的值为 [1,2,3,4]
<div id="sort">
<div v-for="(item,index) in arrData" :key="index" >
<div>{{item}}</div>
</div>
</div>
复制代码
mounted () {
let el = document.getElementById('sort')
var sortable = new Sortable(el, {
onEnd: (e) => {
const tempItem = this.arrData.splice(e.oldIndex, 1)[0]
this.arrData.splice(e.newIndex, 0, tempItem)
}
})
}
复制代码
当然一开始的时候,数据渲染肯定是没有问题的
好了,我们来看下以下的操作:
可以看到,我将3拖到2上面的时候,下面的数据变成了 1342,但是上面视图的还是1234。然后我第四位置拖到第三位置的时候,下面的数据也是生效的,但是上面的数据似乎全部错乱了。很好,我们重现了案发现场。
接着我改了绑定的 key 值,因为这里的例子比较特殊,我们就认为 item 的值都不相同
<div id="sort">
<div v-for="(item,index) in arrData" :key="item" >
<div>{{item}}</div>
</div>
</div>
复制代码
再看效果:
是的,这个时候数据就完全跟视图同步了。
为什么?
先看官方文档中 key 的一句介绍
有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。
之所以会造成上面渲染错误的情况,是因为我们的 key 值不是独特的,比如上面的 key 值,在调整数组顺序后就每一项原来的 key 值都变了,所以导致了渲染错误。
我们先来得出一个结论,用 index 作为 key 值是有隐患的,除非你能保证 index 始终能够能够作为一个唯一的标识
key 值到底有什么用
在 vue2.0 之后,我们不写 key 的话,就会报 warning,那也就是说官方是希望我们写 key 值的,那么 key 到底在 vue 中扮演了什么样的角色?
不使用 key 可以提高性能么 答案是,是的!可以!
先看官方解释:
如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试修复/再利用相同类型元素的算法。使用 key,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
比如现在有一个数组 [1,2,3,4]变成了[2,1,3,4],那么没有 key 的值会采取一种“就地更新策略”,见下图。它不会移动元素节点的位置,而是直接修改元素本身,这样就节省了一部分性能
而对于有 key 值的元素,它的更新方式如下图所示。可以看到,这里它对 DOM 是移除/添加的操作,这span class="hljs-keyword">for (let i = start; i < end; i++) {
const c = oldCh[i]
if (isDef(c) && sameVnode(node, c)) return i
}
}
复制代码
我们可以看到,如果我们有 key 值,我们就可以直接在 createKeyToOldIdx 方法中创建的 map 对象中根据我们的 key 值,直接找到相应的值。没有 key 值,则需要遍历才能拿到。相比于遍历,映射的速度会更快。
key 值是每一个 vnode 的唯一标识,依靠 key,我们可以更快的拿到 oldVnode 中相对应的节点。
参考
第 1 题:写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?
欢迎大家关注我的前端大杂货铺~




