v-if指令用于根据表达式的真假来条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值时被渲染。
<div id="app">
<p v-if="seen">现在你看到我了</p>
</div>
<script>
new Vue({
el: '#app',
data: {
seen: true
}
})
</script>
当 seen 为 false 时,<p> 元素会被完全移除,而不是隐藏。
v-if 可以和 v-else、v-else-if 配合使用:
<div id="app">
<div v-if="type === 'A'">
A 类型
</div>
<div v-else-if="type === 'B'">
B 类型
</div>
<div v-else-if="type === 'C'">
C 类型
</div>
<div v-else>
不是 A/B/C
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
type: 'A'
}
})
</script>
注意:v-else 和 v-else-if 必须紧跟在 v-if 或 v-else-if 后面,否则不会被识别。
<!-- ❌ 错误:中间有其他元素 -->
<div v-if="ok">Yes</div>
<p>其他内容</p>
<div v-else>No</div>
<!-- ✅ 正确:紧密相连 -->
<div v-if="ok">Yes</div>
<div v-else>No</div>
如果想切换多个元素,但又不想添加额外的 DOM 元素,可以使用 <template>:
<template v-if="ok">
<h1>标题</h1>
<p>段落 1</p>
<p>段落 2</p>
</template>
<template> 不会被渲染到最终 DOM 中,只是作为逻辑分组使用。
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头渲染。这可能导致一些意外行为:
<div id="app">
<template v-if="loginType === 'username'">
<label>用户名</label>
<input placeholder="输入用户名">
</template>
<template v-else>
<label>邮箱</label>
<input placeholder="输入邮箱">
</template>
<button @click="toggle">切换</button>
</div>
<script>
new Vue({
el: '#app',
data: {
loginType: 'username'
},
methods: {
toggle: function() {
this.loginType = this.loginType === 'username' ? 'email' : 'username'
}
}
})
</script>
切换登录方式时,输入框中已输入的内容不会清除,因为 Vue 复用了 <input> 元素。
如果需要两个输入框完全独立,添加 key 属性:
<template v-if="loginType === 'username'">
<label>用户名</label>
<input placeholder="输入用户名" key="username-input">
</template>
<template v-else>
<label>邮箱</label>
<input placeholder="输入邮箱" key="email-input">
</template>
现在每次切换,输入框都会重新渲染。
v-show 也可以控制元素显示,但原理不同:
<!-- v-if:条件为假时,元素不存在于 DOM -->
<div v-if="show">v-if 内容</div>
<!-- v-show:条件为假时,元素存在但 display: none -->
<div v-show="show">v-show 内容</div>
选择建议
| 场景 | 推荐 |
|---|---|
| 条件很少改变 | v-if |
| 需要频繁切换 | v-show |
| 运行时条件不可能改变 | v-if |
| 初始条件为假 | v-if(不渲染) |
性能对比:
v-if 有更高的切换开销(需要销毁和重建)v-show 有更高的初始渲染开销(无论真假都会渲染)不推荐在同一元素上使用 v-if 和 v-for。
当它们一起使用时,v-for 优先级更高:
<!-- 这会报错,因为 todo 未定义 -->
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo.text }}
</li>
推荐使用计算属性代替:
computed: {
activeTodos: function() {
return this.todos.filter(function(todo) {
return !todo.isComplete
})
}
}
<li v-for="todo in activeTodos">
{{ todo.text }}
</li>
<div v-if="user.role === 'admin'">
<button @click="deleteAll">删除所有</button>
</div>
<div v-else-if="user.role === 'editor'">
<button @click="edit">编辑</button>
</div>
<div v-else>
<p>您没有操作权限</p>
</div>
<div v-if="loading">
加载中...
</div>
<div v-else-if="error">
加载失败:{{ error }}
</div>
<div v-else>
{{ content }}
</div>
<div v-if="items.length === 0" class="empty-state">
暂无数据
</div>
<div v-else>
<div v-for="item in items" :key="item.id">
{{ item.name }}
</div>
</div>
<div v-if="step === 1">
<h2>第一步:基本信息</h2>
<!-- 表单字段 -->
</div>
<div v-else-if="step === 2">
<h2>第二步:详细信息</h2>
<!-- 表单字段 -->
</div>
<div v-else-if="step === 3">
<h2>第三步:确认</h2>
<!-- 确认信息 -->
</div>
| 指令 | 说明 |
|---|---|
v-if | 条件为真时渲染,为假时销毁 |
v-else-if | 否则如果 |
v-else | 否则 |
key | 强制替换元素而非复用 |
v-if 是真正的条件渲染,适合条件很少改变的场景。