条件渲染:v-if 与 v-show
模板语法与常用指令 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
条件渲染是模板里出现频率最高的逻辑。这一节要分清 v-if 和 v-show 的实现差异与适用场景,掌握 v-else-if / v-else 的配对规则,并知道在「频繁切换」和「首次渲染成本」之间怎么选。
问题场景
一个数据面板有四种状态:加载中、加载失败、空数据、有数据。你用 v-if 串了一串判断,结果发现切换状态时输入框里填的内容丢了;另一个页面的提示条每秒闪一次,用 v-if 切换导致布局抖动。这两个问题的根源都是没分清「销毁重建」和「隐藏显示」。
核心解释
v-if 是真正的条件渲染:条件为假时元素及其子组件被销毁(从 DOM 移除、组件卸载),为真时重新创建。v-show 只是切换 CSS 的 display: none,元素始终在 DOM 里,组件始终挂载。
<script setup>
import { ref } from "vue";
const mode = ref("loading"); // loading | error | empty | ready
const items = ref(["Vue 3 入门", "组合式 API"]);
</script>
<template>
<div v-if="mode === 'loading'">加载中…</div>
<div v-else-if="mode === 'error'">加载失败,请重试</div>
<div v-else-if="mode === 'empty'">还没有内容</div>
<ul v-else>
<li v-for="item in items" :key="item">{{ item }}</li>
</ul>
</template>
v-if / v-else-if / v-else 必须紧邻,中间不能插其他元素(注释可以),否则 v-else 找不到配对。这组指令适合「互斥且不常切换」的状态。
v-show 适合「频繁切换」:
<script setup>
import { ref } from "vue";
const showTip = ref(true);
</script>
<template>
<button @click="showTip = !showTip">切换提示</button>
<p v-show="showTip">这条提示每秒都可能切换,用 v-show 避免重建。</p>
</template>
取舍规则:初始渲染成本 v-if 更低(条件假时什么都不渲染);切换成本 v-show 更低(只改 style)。表单输入、视频播放器、图表实例这类「有内部状态」的内容,频繁切换时用 v-show 才不会丢状态。
还有两个细节。第一,v-if 和 v-for 不要写在同一元素上:Vue 3 里 v-if 优先级更高,拿不到 v-for 的循环变量,正确做法是外面套一层 <template v-for> 或用 computed 先过滤。第二,<template> 标签可以承载 v-if 而不产生额外 DOM 节点,适合包裹多个兄弟元素。
示例拆解
一个可交互的状态切换演示,把四种状态都接上按钮:
<script setup>
import { ref } from "vue";
const mode = ref("loading");
const modes = ["loading", "error", "empty", "ready"];
const search = ref("");
function next() {
const i = modes.indexOf(mode.value);
mode.value = modes[(i + 1) % modes.length];
}
</script>
<template>
<section>
<button @click="next">切换状态(当前:{{ mode }})</button>
<p v-if="mode === 'loading'">加载中…</p>
<p v-else-if="mode === 'error'">加载失败,请重试</p>
<p v-else-if="mode === 'empty'">还没有内容</p>
<div v-else>
<input v-model="search" placeholder="搜索课程" />
<p>你输入了:{{ search || "(空)" }}</p>
</div>
</section>
</template>
验证点:在 ready 状态下输入几个字,切到 loading 再切回 ready,输入框内容被清空——因为 v-if 分支销毁重建了 input。把外层改成 v-show 方案(每个分支一个 <div v-show>)再试一次,输入内容会保留。这个对比就是两种指令的本质区别。
动手任务
- 把上面的例子改成
v-show版本(四个分支各一个div),验证切换后输入内容保留。 - 给「加载中」分支加一个
v-if嵌套:当加载超过 3 秒时显示「网络似乎有点慢」。 - 用
<template v-if>包住一组兄弟元素(标题 + 段落 + 按钮),确认 DOM 里没有多余的包裹节点。
常见错误
v-else前面隔了元素:v-if和v-else之间插了一个<p>,导致v-else失效并报编译警告。- 用
v-if控制频繁切换的组件:每次切换都销毁重建,组件内部状态(滚动位置、输入值、图表实例)全丢,还会带来性能抖动。 v-if与v-for同元素:Vue 3 中v-if先求值,访问不到循环变量,直接报错或渲染异常。- 以为
v-show不支持<template>:v-show不能用在<template>上(没有真实 DOM 可隐藏),只能用在元素上。
检查与复盘
- 能说出
v-if与v-show在 DOM、组件生命周期、切换成本上的三点差异 - 能写出四种状态互斥的
v-if链,并说明为什么不能插元素 - 能判断一个场景该用哪个指令,并给出理由
参考资料
- Vue 官方文档「条件渲染」:
v-if与v-show的官方对比 - Vue 官方文档「列表渲染」:
v-if与v-for的优先级说明
读完这一课了?
标记完成后,工作台会安排接下来的学习。