浅析v-if与v-show的区别
2024-12-09 11:13:16

典中典的面试题,属于我刚入门的时候在问,到现在几年了,还在问。

这么多年了,面试官真是一点进步都没有。

正文

无论是vue3还是vue2,关于这些个简单的vue指令变动都是不大的,这里就直接以v3的源码为例子,看看这块到底怎么个事儿。

源码浅析

vue的大致渲染流程如下,这里不详细展开说,改天开单章细说。

  • 将模板template转为ast结构的JS对象
  • ast得到的JS对象拼装renderstaticRenderFns函数
  • renderstaticRenderFns函数被调用后生成虚拟VNODE节点,该节点包含创建DOM节点所需信息
  • vm.patch函数通过虚拟DOM算法利用VNODE节点创建真实DOM节点

v-show原理

不管初始条件是什么,元素总是会被渲染。

这里我们看一下在vue源码是如何实现的。

代码很好理解,有transition就执行transition,没有就直接设置display属性

这里我们也看到了,v-show只是执行了挂载和更新的相关周期,所以并非是全生命周期的方法。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// https://github.com/vuejs/vue-next/blob/3cd30c5245da0733f9eb6f29d220f39c46518162/packages/runtime-dom/src/directives/vShow.ts
export const vShow: ObjectDirective<VShowElement> = {
beforeMount(el, { value }, { transition }) {
el._vod = el.style.display === 'none' ? '' : el.style.display
if (transition && value) {
transition.beforeEnter(el)
} else {
setDisplay(el, value)
}
},
mounted(el, { value }, { transition }) {
if (transition && value) {
transition.enter(el)
}
},
updated(el, { value, oldValue }, { transition }) {
// ...
},
beforeUnmount(el, { value }) {
setDisplay(el, value)
}
}

v-if原理

v-if在实现上比v-show要复杂的多,因为还有else else-if 等条件需要处理,这里我们也只摘抄源码中处理 v-if 的一小部分

返回一个node节点,render函数通过表达式的值来决定是否生成DOM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// https://github.com/vuejs/vue-next/blob/cdc9f336fd/packages/compiler-core/src/transforms/vIf.ts
export const transformIf = createStructuralDirectiveTransform(
/^(if|else|else-if)$/,
(node, dir, context) => {
return processIf(node, dir, context, (ifNode, branch, isRoot) => {
// ...
return () => {
if (isRoot) {
ifNode.codegenNode = createCodegenNodeForBranch(
branch,
key,
context
) as IfConditionalExpression
} else {
// attach this branch's codegen node to the v-if root.
const parentCondition = getParentCondition(ifNode.codegenNode!)
parentCondition.alternate = createCodegenNodeForBranch(
branch,
key + ifNode.branches.length - 1,
context
)
}
}
})
}
)

面试回答

以下算是教科书的回答,个人觉得很经典,可以考虑再搭配一点场景来辅助记忆。

虽然 v-ifv-show 都可以用于根据条件展示元素,但它们背后的实现机制和适用场景有所不同。

当然,面试时候最好不要回答源码相关,除非你对源码有极深的理解,不然面试官会以为你对源码研究颇深,甚至以为你对源码有贡献。

一旦问题脱缰,装到了面试官擅长的领域,到时候容易控不住场,所以,要适度装逼。

  • 渲染机制v-if 是条件性地渲染元素,即如果条件不满足,元素甚至不会被添加到 DOM 中;而 v-show 元素无论条件如何始终会被渲染到 DOM 中,只是通过切换 display 属性来控制显示。
  • 性能考虑:因为 v-if 控制的元素不会被频繁切换或者在初始化时条件就不成立,使用 v-if 更为合适,避免了不必要的渲染成本。而 v-show 适用于需要频繁切换显示状态的场景,因为元素始终保持在 DOM 中,切换开销更小。
  • 编译过程v-if 是真正的条件渲染,因为它确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。而 v-show 只是简单地切换元素的 display CSS 属性。
  • 使用场景:当你的条件很少改变时,推荐使用 v-if;如果需要非常频繁地切换,使用 v-show 会更好,但是实际开发过程中,大多数业务场景不缺这点性能,这种场景我们很难见到。

个人拓展

但是,实际开发过程中,大多数使用v-if来做处理问题。

  • v-if因为将元素完成了移除,这样能够较为完全的注销子组件,这样,子组件内的生命周期是从created触发的,如果子组件中有什么方法一定要写在created中,那么这里就非常推荐使用v-if了。
  • v-show无法配合v-else做条件渲染,如果有多个条件渲染,v-if显然比v-show的适用面更广一些。
  • template支持v-if,但却不支持v-show,有些时候,可以使用template配合v-if来写组件切换的效果,有些组件库中也有很多template打头的插槽,这里推荐使用v-if来写,而不是v-show。

结语

v-show与v-if的区别,新手都知道的问题,但是将这种简单的问题回答的出彩,也很重要。

参考

v-show和v-if的区别 以及应用场景 - 掘金 (juejin.cn)