v-show 和 v-if 的区别以及应用场景
Category(分类): Vue Status: 已核对

一、v-show 与 v-if 的共同点
在 Vue 中,v-show 与 v-if 都能根据表达式控制内容是否可见:
<section v-show="isShow">v-show 内容</section>
<section v-if="isShow">v-if 内容</section>
表达式为 true 时,两者通常都显示并占据页面空间;为 false 时,两者通常都不占据空间。但这只是视觉结果相似:v-if 的条件块可能根本不存在,v-show 的 DOM 则始终存在,只是被设为 display: none。
二、v-show 与 v-if 的区别
控制方式与切换成本
v-if是真正的条件渲染。初始条件为假时不会创建条件块;条件改变时,运行时渲染器会挂载或卸载对应的 VNode 子树,同时正确建立或清理内部组件、指令和事件监听器。v-show总会创建元素,之后主要通过内联display切换可见性,并在显示时恢复元素原有的display值。
模板只在构建或运行时编译模板的阶段编译,并不是每次 v-if 切换都“局部编译”。Vue 3 编译器会为 v-if 生成条件分支,运行时 renderer 再执行 patch、mount 或 unmount;v-show 则被编译为运行时指令。
生命周期、状态与副作用
对普通、未被缓存的子组件:
v-if初始为假时不会创建组件;变为真时会执行setup()、Options API 的created、beforeMount、mounted等初始化和挂载流程;变回假时执行beforeUnmount、unmounted。Vue 2 历史名称是beforeDestroy、destroyed。v-show即使初始为假也会挂载组件。之后显示和隐藏不会卸载组件,也不会因切换而重新执行组件挂载/卸载钩子。
因此,v-if 通常会重置组件局部状态,并让同步创建、由 Vue 管理的 effect/watch 随卸载停止;手动创建的定时器、DOM 监听器和连接仍应在卸载钩子中清理。v-show 会保留输入值、组件实例和 DOM 状态,但隐藏不等于暂停:响应式更新、轮询或订阅仍可能继续运行并占用内存。
模板和组件限制
v-if 支持 <template> 条件块,以及 v-else-if、v-else:
<template v-if="isShow">
<h2>标题</h2>
<p>多个节点的条件块</p>
</template>
<p v-else>暂无内容</p>
v-show 不支持 <template>,也不能与 v-else 配合。隐藏多个节点时需要真实包裹元素:
<div v-show="isShow">
<h2>标题</h2>
<p>多个节点共用一个真实根元素</p>
</div>
把 v-show 写在组件上时,指令实际作用于组件渲染出的根 DOM,而不是“隐藏组件实例”。它只适合最终渲染为单一真实元素根的组件;Vue 3 多根 Fragment 或非元素根组件可能产生警告并忽略指令。若语义是创建/销毁组件,应使用 v-if;若是动态组件缓存,应评估 <KeepAlive>。
三、v-show 与 v-if 原理分析
大致渲染流程如下:
- 编译器把模板转换为 AST,再生成渲染函数。
- 渲染函数创建 VNode;Vue 3 编译器同时标记动态节点,以便运行时跳过不必要的工作。
- renderer 根据 VNode 执行 patch,创建、更新或卸载真实 DOM。
v-show 原理
Vue 3 当前实现会保存元素原有的 display,并通过 beforeMount、updated、beforeUnmount 等指令钩子切换显示状态。存在 <Transition> 时,v-show 还会接入过渡钩子;它并非给元素永久写死一个 CSS 值。
源码可参阅:
v-if 原理
v-if 需要处理 if、else-if 和 else 分支。编译器把它们转换成条件分支,运行时依据条件选择要 patch 的 VNode 树;切走普通分支时会卸载其 DOM、指令和子组件。
源码可参阅 compiler-core/src/transforms/vIf.ts。内部源码可能随版本变化,公共行为应以官方文档为准。
四、与 Transition 配合
<Transition> 支持由 v-if、v-show、动态组件或 key 触发的进入/离开。默认插槽应只有一个元素或组件根:
<Transition name="fade">
<p v-if="isShow">创建或销毁节点</p>
</Transition>
<Transition name="fade">
<p v-show="isShow">保留节点并切换 display</p>
</Transition>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
v-show 的离开过渡完成后才会设置 display: none;过渡不会暂停隐藏组件中的业务副作用。
五、使用场景与选择表
| 需求 | 更合适的选择 | 原因与代价 |
|---|---|---|
| 初始可能不显示,且子树很重 | v-if | 惰性创建;再次隐藏会卸载并丢失局部状态 |
| 高频打开/关闭,需保留输入和 DOM 状态 | v-show | 切换成本较低;初始渲染、内存和隐藏期间副作用成本更高 |
| 需要真正释放大子树、连接或监听器 | v-if | 卸载组件;手动资源仍须在卸载钩子清理 |
| 动态组件切换并保留实例 | <KeepAlive> | 使用 activated/deactivated 管理缓存与资源,不等同于隐藏 |
多节点条件块或需要 v-else | v-if | 支持 <template>、v-else-if、v-else |
“高频用 v-show、很少变化用 v-if”是有用的经验,但不是绝对 benchmark。还应根据子树大小、初始成本、状态是否重置,以及隐藏期间副作用是否允许继续运行来选择。
官方参考
作者:你还犟
链接:https://juejin.cn/post/7105405430809296904 来源:稀土掘金。著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。