一、前言
进入模板与指令篇。第一个需求特别常见:登录了显示用户中心,没登录显示登录按钮;这个面板要显示,那个不要。Vue 提供两个指令干这事:v-if 和 v-show。功能看着一样,底层完全不同,它们的区别是高频面试题,今天一次讲透。
二、v-if:真·增删 DOM
v-if="表达式":表达式为真,元素渲染;为假,元素从 DOM 里彻底消失:
vue
<template>
<p v-if="isLogin">欢迎回来,尊贵的用户</p>
<button @click="isLogin = !isLogin">切换登录状态</button>
</template>
<script setup>
import { ref } from 'vue'
const isLogin = ref(false)
</script>
没登录时按 F12 看一下:那个 <p> 标签根本不存在。v-if 是"不满足就不造出来",切来切去就是真实地创建、销毁元素。
三、v-else-if 与 v-else:多分支链
和 JS 的 if/else if/else 一样,可以串成一串:
vue
<template>
<p v-if="score >= 90">优秀 🏆</p>
<p v-else-if="score >= 60">及格 💪</p>
<p v-else>不及格,重修吧 📚</p>
</template>
<script setup>
import { ref } from 'vue'
const score = ref(75)
</script>
铁律:v-else-if / v-else 必须紧跟在 v-if(或上一个 v-else-if)元素的后面,中间插了别的元素就断链报错。
四、v-show:假的隐藏,真的"css 隐身术"
v-show 效果一样,但机制完全不同------元素永远在 DOM 里 ,只是切换 CSS 的 display:
vue
<template>
<p v-show="isVip">VIP 专享内容</p>
</template>
v-show 为 false 时按 F12:标签还在,只是多了 style="display: none;"。
五、核心区别对比(面试高频)
| 对比项 | v-if | v-show |
|---|---|---|
| 本质 | 真实创建 / 销毁 DOM | 一直渲染,切换 display |
| 初始渲染 | 条件为 false 时不渲染(惰性) | 无论真假都渲染 |
| 切换开销 | 大(创建/销毁) | 小(只改 CSS) |
| 初始开销 | 小(不显示就不渲染) | 大(永远渲染) |
| 支持 template 包裹 | ✅ | ❌ |
| 配 v-else 链 | ✅ | ❌ |
选型口诀一句话:切换频繁用 v-show,条件很少变用 v-if。
场景对号入座:
- 登录/未登录的大区块、权限模块 → v-if(几乎不切换,false 时还能省渲染)
- Tab 页签、弹窗这类高频开关 → v-show(来回切换不重新创建)
六、v-if 的一个隐藏福利:组件生命周期
v-if 切走时,里面的组件会被真正销毁(触发 onUnmounted),切回来重新创建(重新触发 onMounted)。这个特性可以用来"重置"一个组件的状态。配合 keep-alive 缓存组件是 030 篇的主题,这里先知道行为差异。
七、综合实战:权限提示 + Tab 切换
两个指令各干最适合的活:
vue
<template>
<!-- v-if:登录态大区块,几乎不变 -->
<div v-if="isLogin" class="panel">
<p>👤 {{ username }},欢迎回来</p>
<!-- v-show:tab 高频切换 -->
<div class="tabs">
<button @click="activeTab = 'post'">我的帖子</button>
<button @click="activeTab = 'star'">我的收藏</button>
</div>
<div v-show="activeTab === 'post'">📝 帖子列表内容</div>
<div v-show="activeTab === 'star'">⭐ 收藏列表内容</div>
</div>
<p v-else>请先登录后查看 <button @click="isLogin = true">去登录</button></p>
</template>
<script setup>
import { ref } from 'vue'
const isLogin = ref(false)
const username = ref('小明')
const activeTab = ref('post')
</script>
品味分工:登录区块用 v-if(低频切换 + 严格互斥,配了 v-else);Tab 内容用 v-show(高频来回切,不销毁重建)。
【截图位置:未登录提示、登录后 Tab 切换的运行效果】
八、踩坑记录
- v-if 和 v-for 写在同一元素 :Vue3 里 v-if 优先级更高,此时访问不到 v-for 的 item,直接报错。正确做法:用 computed 先过滤数组,或外层包 template 写 v-for
- v-show 想配合 template:不行,template 不是真实 DOM 没法挂 display,只能用 v-if
- v-else 断链:中间插了别的元素,报"v-else 没有相邻的 v-if"
- v-if 里放昂贵组件:条件为 false 时确实不渲染(这是优点),但每次切换都完整销毁重建,高频操作的场景误用 v-if 会明显卡顿
- 初始不显示的弹窗用了 v-show:弹窗内所有内容(哪怕从不打开)都会被初始渲染,白白多花时间;低频弹窗建议 v-if
九、今日小结
- v-if:真实增删 DOM,惰性渲染,支持 v-else 链和 template
- v-show:CSS 显隐,永远渲染,切换零成本
- 口诀:频繁切换 v-show,少变条件 v-if
- v-if 会销毁/重建内部组件,可用来重置状态
作者:陈志宝
下篇预告
页面批量展示数据离不开列表:任务清单、商品列表、表格行。下一篇 v-for 列表渲染------顺便讲透那个经典面试题:key 到底是干嘛的?为什么不能用 index 当 key?