首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用 :key 管理可复用元素

用 :key 管理可复用元素

作者头像
Chor
发布2019-11-07 18:14:08
5600
发布2019-11-07 18:14:08
举报
文章被收录于专栏:前端之旅前端之旅

input 中的 key

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

<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>

我们会发现,在点击按钮切换登录方式后,输入框中已有的内容没有被清除,这是为什么呢?

引用官方文档的原话:

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

这里的 input 实际上复用了切换之前的 input。而类似 <input><select><textarea> 这样的表单元素都有一个 internal state 保存着元素的值,在元素复用时,这个值是会得到保留的。

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

接着再来看一个利用 v-for 生成 input 的例子。 假如我们的代码为:

<div id="app">
    <div v-for="(item,index) in array">
        {{item}}: <input type="text"> 
    </div>
</div>
const app = new Vue({
    el:'#app',
    data:{
        array:["A","B","C","D","E"]
    }
})

之后生成的 input 中我们填入字符串作为 internal state。如图:

在没有使用 key 的情况下,我们通过 app.array.splice(2,0,"F") 在 BC 之间插入 F,发现:

和之前一样,因为 Vue 采用的是 就地复用 策略,这意味着 ABCDE 在原地不动的情况下被复用了,CDE 都被重新渲染了一次,但先前的 internal state 仍然保留着。

出于性能考虑,有没有办法可以只移动个别元素,单独渲染要插入的那个新元素呢?有了前面的经验,我们会想到给每个 input 一个 key 值。

首先我们尝试将 index 作为 key,之后进行插入操作,发现:

问题依然存在。这是因为,我们将 index 作为复用的判断依据,相当于告诉 Vue:“只要这两个东西的 index 一样,就进行复用”。插入之前 C 的 index 是 2,插入之后 F 的 index 也是 2,于是 F 复用了 C,同理,DE 也被复用了,并因此重新渲染了一次。

index 是会随着插入删除改变的值,所以它实际上并不适合作为 key。于是我们想:在进行插入或者删除操作的时候,有没有一种值始终不会改变呢?有的,我们可以给每个元素一个单独的 id。但更简单的方法是直接使用 item,即元素本身的值,毕竟这个值对每个元素来说也是独一无二的。

我们将 item 作为 key ,之后进行插入操作,发现:

这回正常了。可以很明显地看到,每个元素都复用了先前的对应元素,这是因为此时 item (即元素值)才是复用的判断依据,相当于告诉 Vue:“只要这两个东西的元素值一样,就进行复用”。例如对于 C 来说,它只会复用与自己的值一样的元素,显然这个元素就是 C 本身。同理,D 复用 D,E 复用 E,CDE 都不需要重新渲染了,只需要后移以方便 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://stackoverflow.com/questions/44077320/what-is-the-use-of-track-by-or-key-in-v-for-in-vue-js https://juejin.im/post/5aae19aa6fb9a028d4445d1a#comment

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-09-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • input 中的 key
  • Virtual DOM 的 Diff 算法
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档