引用 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 更新已渲染的元素列表时,默认用 就地复用 策略。列表数据修改的时候,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 元素即可。
下面大致从虚拟DOM的Diff算法实现的角度去解释一下。
vue 和 react的虚拟 DOM 的 Diff 算法大致相同,其核心是基于两个简单的假设:
引用 React’s diff algorithm 中的例子:
当某一层有很多相同的节点时,也就是列表节点时,Diff 算法的更新过程默认情况下也是遵循以上原则。 比如一下这个情况:
我们希望可以在 B 和 C 之间加一个 F,Diff 算法默认执行起来是这样的:
即把 C 更新成 F,D 更新成 C,E 更新成 D,最后再插入 E,这样显然很没有效率。
所以我们需要使用 key 来给每个节点做一个唯一标识,Diff 算法就可以正确的识别此节点,找到正确的位置区插入新的节点。
所以 key 的作用主要是为了高效的更新虚拟 DOM。
就目前所了解的情况,key的作用有以下这些。v-for遍历时,用id,uuid之类作为key,唯一标识节点加速虚拟DOM渲染,响应式系统没有监听到的数据,用+new Date()生成的时间戳作为key,手动强制触发重新渲染
尤大在vue 2.x的文档中明确指出:建议尽可能在使用 v-for 时提供 key attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!