为什么应该在 v-for 中使用 :key?

更新日期: 2019-09-26阅读: 2.7k标签: key


input 中的 key

引用 vue 官方文档的原话:

Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。

这样容易导致一些问题。因为类似 <input> , <select> , <textarea> 这样的元素都有一个internal state 保存着元素的值,而在元素复用时,这个值是会得到保留的。

我们来看一个登陆方式切换的例子:

<div v-if="isUser">
    <label>Login with account</label>
    <input type="text" placeholder="Enter your account">
</div>
<div v-else>
    <label>Login with email</label>
    <input type="text" placeholder="Enter your email">
</div>

<button @click="isUser=!isUser">click to toggle</button>

我们会发现,在点击按钮切换登录方式的时候,输入框中已有的内容 不会被清除 ,这是因为input 的 internal state 保留着元素的值。

如果我们希望切换的时候不保留这个值呢?我们可以给两个 input 添加不同的 :key 。因为 vue 是将 key 作为唯一标识从而来识别复用的元素的,如果两个元素的 key 不同,那么就相当于告诉 vue“这两个元素是完全独立的,不要复用它们”。


v-for 中的 key

同样的,使用 v-for 更新已渲染的元素列表时,默认用 就地复用 策略。列表数据修改的时候,vue 会根据 key 去判断某个值是否修改 —— 如果修改,则重新渲染这一项,否则复用之前的元素。在 v-for 中使用 key 是一个最佳实践,但是我们需要注意使用的是什么 key 。

假如我们想要在如下数组 [A,B,C,D,E] 的 B 和 C 之间插入 F:

index   id    Array.elem
0        1       A
1        2       B
2        3       C
3        4       D
4        5       E

如果是使用 index 作为 key :

  • 在末尾插入 F 的话没有问题,因为这个时候不影响前面元素的 index,每个元素的 index 不变,而 vue 可以依据这些 index 对元素进行复用;但是现在是在中间插入 F,一旦插入成功,那么 CDE 的 index 都会改变,这时候 CDE 都需要重新渲染一次。而 AB 的 index 是不变的,所以 AB 可以得到复用。

如果是使用 id 作为 key :

  • 这里就要注意了,这个 id 是唯一的,也是固定不变的(也可以采用元素本身的值作为这个唯一的 id),不管插入还是删除,元素该是哪个 id 还是哪个 id ,这意味着以这样的 id 作为key 时,所有旧元素都可以得到复用。所以这种情况下,我们只需要渲染新插入的 F 元素即可。


Virtual dom 的 Diff 算法

下面大致从虚拟DOM的Diff算法实现的角度去解释一下。

vue 和 react的虚拟 DOM 的 Diff 算法大致相同,其核心是基于两个简单的假设:

  • 两个相同的组件产生类似的DOM结构,不同的组件产生不同的DOM结构。
  • 同一层级的一组节点,他们可以通过唯一的id进行区分。基于以上这两点假设,使得虚拟DOM的Diff算法的复杂度从O(n^3)降到了O(n)。

引用 React’s diff algorithm 中的例子:


当某一层有很多相同的节点时,也就是列表节点时,Diff 算法的更新过程默认情况下也是遵循以上原则。 比如一下这个情况:


我们希望可以在 B 和 C 之间加一个 F,Diff 算法默认执行起来是这样的:


即把 C 更新成 F,D 更新成 C,E 更新成 D,最后再插入 E,这样显然很没有效率。

所以我们需要使用 key 来给每个节点做一个唯一标识,Diff 算法就可以正确的识别此节点,找到正确的位置区插入新的节点。


所以 key 的作用主要是为了高效的更新虚拟 DOM。


链接: https://www.fly63.com/article/detial/5562

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!