条件渲染是 Vue 中最基础也最常用的指令之一,它让你能够根据数据的真假,控制元素的显示与隐藏。Vue 提供了两套机制:以 v-if 为核心的“条件判断”系列,以及独立的 v-show。它们的用法相似,但底层行为和适用场景截然不同。
v-if / v-else-if / v-else:真正的条件渲染
v-if 指令会“真实地”销毁或重建 DOM 元素。它的表达式为 true 时,元素被挂载到 DOM 树;为 false 时,元素及其子节点会被完全移除(连同内部的事件监听器和组件实例都会被销毁)。
你可以搭配 v-else-if 和 v-else 使用,形成类似 if…else if…else 的逻辑:
<template>
<div>
<p v-if="score >= 90">优秀</p>
<p v-else-if="score >= 60">及格</p>
<p v-else>不及格</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const score = ref(85)
</script>
必须注意:这几个指令必须紧邻使用,中间不能插入其他元素。如果你在 v-if 和 v-else 之间塞了一个 <div>,Vue 会报错。
用 key 管理可复用元素
Vue 为了渲染效率,会尽量复用已有的 DOM 元素。比如在两个 v-if 分支中都包含相同的 <input>,切换分支后输入框中的文字可能会保留——因为 Vue 复用了同一个 <input>。如果你希望每次切换都“重新开始”,可以给元素添加不同的 key 属性:
<template>
<div>
<div v-if="loginType === 'username'">
<input placeholder="输入用户名" key="username-input">
</div>
<div v-else>
<input placeholder="输入邮箱" key="email-input">
</div>
<button @click="toggle">切换登录方式</button>
</div>
</template>
加上 key 后,两个 <input> 被视为完全独立的元素,切换时旧元素被销毁、新元素被创建,输入内容也就清空了。
v-show:始终存在,只是切换显示
v-show 的语法和 v-if 类似:
<p v-show="isVisible">我长得像 v-if,但其实不一样</p>
但无论 isVisible 是 true 还是 false,这个 <p> 元素始终会被渲染并保留在 DOM 中。v-show 做的仅仅是在条件为假时,给元素添加 display: none 的内联样式(!important 优先级很高)。这意味着:
- 元素的初始化开销始终存在(首次渲染就挂载了)。
- 切换时几乎没有开销,仅仅改个样式。
- 因为元素一直在 DOM 中,所以组件内的响应式数据、计算属性、侦听器会保持激活状态。
v-show无法和v-else连用,它没有v-else-show这样的亲属。
v-if vs v-show:如何选择
下面这张表直接对比了两者的核心差异,帮你快速判断:
| 特性 | v-if | v-show |
|------|------|--------|
| 条件为假时的 DOM | 元素被移除,不占据任何空间 | 元素依然存在,但 display: none |
| 切换开销 | 高(需要销毁/重建,触发生命周期钩子) | 低(仅 CSS 切换) |
| 初始渲染开销 | 低(条件假时什么都不做) | 高(无论如何都会渲染) |
| 生命周期钩子 | 随元素创建/销毁而触发 | 不触发销毁,只有挂载一次 |
| 适用场景 | 运行时条件很少改变,或对初始性能敏感 | 频繁切换的场景,如弹窗、菜单、选项卡 |
典型经验总结:
- 用 v-if:在权限判断、根据用户角色显示不同模块、一次性配置等场景,因为条件一旦确定就不太会变。比如:
<AdminPanel v-if="user.role === 'admin'" />
- 用 v-show:在用户交互中需要实时、高频切换的场景,比如下拉菜单的展开/收起、对话框的显示/隐藏、tab 切换。这些情况下,如果频繁销毁重建元素,不仅浪费性能,组件内的滚动位置、表单填写状态等也会丢失。
- 一个常见坑:
v-if和v-for不要放在同一个元素上。因为v-for优先级比v-if更高,会导致每次循环都执行一次条件判断,严重影响性能。正确的做法是用计算属性先过滤数据,再用v-for遍历已过滤的列表。
原理简析(帮助理解)
Vue 在编译阶段会把 v-if 转换成一个三元表达式,运行时根据条件动态创建或销毁 VNode;而 v-show 仅仅是给生成的 VNode 附加了一个指令,patch 阶段会根据指令绑定直接修改元素的 style.display。这也就是为什么 v-show 没有 v-else 搭配——它只是同一个元素上的一个“样式开关”,不涉及多分支节点选择。
理解两者的区别后,在合适的场景选择正确的指令,可以让页面交互更流畅,代码逻辑也更清晰。