一、v-if 和 v-show 的区别
区别: |
v-if |
v-show |
手段 |
是通过控制dom节点的存在与否来控制元素的显隐; |
是通过设置DOM元素的display样式,block为显示,none为隐藏; |
编译过程 |
切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件; |
只是简单的基于css切换; |
编译条件 |
是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始局部编译(编译被缓存后,然后再切换的时候进行局部卸载); |
是在任何条件下(首次条件是否为真)都被编译,然后被缓存,而且DOM元素保留; |
性能消耗 |
有更高的切换消耗; |
有更高的初始渲染消耗; |
需要注意的是:
1、v-show 不支持 <template> 元素!
2、v-for的优先级是高于v-if的,不推荐同时使用。
二、v-if 的局部编译/卸载问题
我们在使用 v-if 时,v-if 为 false 时会完全销毁该元素,为 true 时在重新创建该元素,但实际上 vue 为了更高效的渲染元素,通常会对相同的元素进行复用,而不是完全重新渲染。
我们可能会遇到这样的场景:
<template v-if="type === 'username'">
<label>用户名</label>
<input placeholder="Enter your username">
</template>
<template v-else>
<label>邮箱</label>
<input placeholder="Enter your email address">
</template>
1、默认type值为 'username' 时, 页面上显示一个用户名输入框;
2、我们在输入框中输入 “张三”,此时用户名输入框内容为 “张三”;
3、然后修改type的值为 'email' 时,页面上用户名输入框变成了邮箱输入框;此时会发现,邮箱输入框中的内容仍为 “张三”。
F12查看元素时会发现,label的内容变了,placeholder的内容变了,但是input值并没有发生变化。这就是vue的局部编译导致的。
此时涉及到虚拟DOM的问题,虚拟DOM本质上是用原生js对象去描述一个DOM节点,是对真实DOM的一种抽象。vue在对元素进行重新渲染时,首先会通过 diff 算法,比较两个虚拟 DOM 的差异,然后通过 pach 算法,将两个虚拟 DOM 的差异应用到真实DOM中,实现元素的切换。
所以 v-if 真正销毁和重建的是有差异的那一部分。
回到刚才的问题,两个模板使用了相同的元素,所以差异就是label的内容,以及input的placeholder,而元素不会重新渲染,所以input的值也就不会发生变化。
三、Key 的使用
那么我们要如何完全更新模板中的元素,此时 Vue 提供了一种方式来表达“这两个元素是完全独立的,不需要复用”。只需添加一个具有唯一值的 key 即可:
key 是 Vue 中 Vnode中的唯一标记,通过这个key,我们的 diff 操作可以更准确、更快速
1、更准确:因为带key就不会就地复用,在sameNode函数 a.key === b.key 对比中可以避免就地复用的情况。
2、更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快。
所以刚才的场景,可以改成
<template v-if="type === 'username'">
<label>用户名</label>
<input placeholder="Enter your username" key="username">
</template>
<template v-else>
<label>邮箱</label>
<input placeholder="Enter your email address" key="email">
</template>
这样再切换用户名和邮箱时,就不会复用input了,因为两个input的拥有不同的key。注意:此时label元素还是会复用,因为label元素没有加key。
注:不建议用index作为key,因为不管数组的顺序怎么颠倒,index都是0,1,2这样排列,导致Vue会复用错误的旧子节点,做很多额外工作。