61

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

 4 years ago
source link: https://www.tuicool.com/articles/yMZrIfb
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

6R3IfeY.jpg!web

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>

我们会发现,在点击按钮切换登录方式的时候,输入框中已有的内容 不会被清除 ,这是因为 inputinternal 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 中的例子:

ZzeYnem.jpg!web

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

2YZVZvR.jpg!web

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

VRVRbev.jpg!web

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

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

6R3IfeY.jpg!web

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK