v-if 和 v-show 的区别以及 v-if 局部编译/卸载问题

一、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会复用错误的旧子节点,做很多额外工作。

猜你喜欢

转载自blog.csdn.net/Hello_MrShu/article/details/127107857