共计 1235 个字符,预计需要花费 4 分钟才能阅读完成。
看文章后发现 key 便于理解的两段话,特此记录一下。
1、加 key 与不加 key 的区别
对实现列表的增删,如果给列表的每一项增加一个 key,即唯一索引,那就可以很清楚的知道两个列表谁少了谁没变。而如果不加 key 的话,就只能一个个对比,然后进行操作。
2、理解 key
见 vue/patch.js,在不带 key 的情况下,判断 sameVnode 时因为 a.key 和 b.key 都是 undefined,对于列表渲染 来说已经可以判断为相同节点然后调用 patchVnode 了,实际根本不会进入到答主给的 else 代码,也就无从谈起“带 key 比不带 key 时 diff 算法更高效”了。
然后,官网推荐推荐的使用 key,应该理解为“使用唯一 id 作为 key”。因为 index 作为 key,和不带 key 的效果是一样的。index 作为 key 时,每个列表项的 index 在变更前后也是一样的,都是直接判断为 sameVnode 然后复用。(这段话很重要)
说到底,key 的作用就是 更新组件 时判断两个节点是否相同。相同就复用,不相同就删除旧的创建新的。
正是因为带唯一 key 时每次更新都不能找到可复用的节点,不但要销毁和创建 vnode,在 DOM 里添加移除节点对性能的影响更大。所以会才说“不带 key 可能性能更好”。看下面这个实验,渲染 10w 列表项,带唯一 key 与不带 key 的时间对比:
不使用 key 的情况:
<li v-for="item in list">{{item.text}}</li>
使用 id 作为 key 的情况:
<li v-for="item in list" :key="item.id">{{n.text}}</li>
list 构造:
const list1 = []
const list2 = []
for (let i = 0; i <= 100000; i++) {
list1.push({
id: i,
text: i
})
list2.push({
id: i * 2,
name: 100000 - i
})
}
因为不带 key 时节点能够复用,省去了销毁 / 创建组件的开销,同时只需要修改 DOM 文本内容而不是移除 / 添加节点,这就是文档中所说的“刻意依赖默认行为以获取性能上的提升”。
既然如此,为什么还要建议带 key 呢?因为这种模式只适用于渲染简单的无状态组件。对于大多数场景来说,列表组件都有自己的状态。
举个例子:一个新闻列表,可点击列表项来将其标记为 ” 已访问 ”,可通过 tab 切换“娱乐新闻”或是“社会新闻”。
不带 key 属性的情况下,在“娱乐新闻”下选中第二项然后切换到“社会新闻”,” 社会新闻 ” 里的第二项也会是被选中的状态,因为这里复用了组件,保留了之前的状态。要解决这个问题,可以为列表项带上新闻 id 作为唯一 key,那么每次渲染列表时都会完全替换所有组件,使其拥有正确状态。
这只是个简单的例子,实际应用会更复杂。带上唯一 key 虽然会增加开销,但是对于用户来说基本感受不到差距,而且能保证组件状态正确,这应该就是为什么推荐使用唯一 id 作为 key 的原因。至于具体怎么使用,就要根据实际情况来选择了。