| 特性 | v-if | v-show |
|---|---|---|
| DOM操作 | 条件性地渲染或销毁DOM元素 | 仅切换CSS的display: none |
| 初始渲染成本 | 条件为假时不渲染,初始成本低 | 无论条件真假都会渲染,初始成本高 |
| 切换开销 | 切换时涉及DOM创建/销毁,开销大 | 仅切换CSS属性,开销小 |
| 生命周期 | 切换时触发组件的创建/销毁生命周期 | 组件始终存在,只控制显示隐藏 |
| 编译阶段 | 惰性的,条件为假时不编译 | 无论条件真假都会编译 |
| 与v-else配合 | ✅ 支持 | ❌ 不支持 |
与<template> |
✅ 支持 | ✅ 支持 |
<template>
<div>
<!-- 条件为假时,元素完全不存在于DOM中 -->
<div v-if="isVisible">使用v-if控制显示</div>
<!-- 支持v-else-if和v-else -->
<div v-if="type === 'A'">类型A</div>
<div v-else-if="type === 'B'">类型B</div>
<div v-else>其他类型</div>
<!-- 配合<template>包裹多个元素 -->
<template v-if="showSection">
<h2>标题</h2>
<p>内容</p>
</template>
</div>
</template>
<script>
export default {
data() {
return {
isVisible: false,
type: 'A',
showSection: true
}
}
}
</script>
<template>
<div>
<!-- 条件为假时,元素存在但通过display:none隐藏 -->
<div v-show="isVisible">使用v-show控制显示</div>
<!-- 不支持v-else -->
<!-- 错误写法: <div v-show="condition">内容</div><div v-show="!condition">其他</div> -->
<!-- 可以用在任意元素上 -->
<component-a v-show="showComponent" />
<tr v-show="hasData">
<td>数据行</td>
</tr>
</div>
</template>
<script>
export default {
data() {
return {
isVisible: false,
showComponent: true,
hasData: true
}
}
}
</script>
<template>
<div>
<!-- 初始不渲染,减少首屏DOM节点数 -->
<heavy-component v-if="user.isPremium" />
<!-- 权限控制:无权限时不渲染 -->
<admin-panel v-if="user.role === 'admin'" />
<!-- 数据依赖:数据未加载时不渲染 -->
<chart-component v-if="dataLoaded && data.length > 0" />
</div>
</template>
2. 需要完整生命周期控制
<template>
<div>
<!-- 组件需要执行created/mounted等初始化逻辑 -->
<live-chat v-if="chatEnabled" />
<!-- 切换时需要重新初始化的组件 -->
<form-editor v-if="editMode" :key="formId" />
</div>
</template>
3. 条件分支逻辑
<template>
<div>
<!-- 清晰的if-else逻辑 -->
<div v-if="status === 'loading'">
<loading-spinner />
</div>
<div v-else-if="status === 'error'">
<error-message :error="error" />
</div>
<div v-else>
<content-display :data="data" />
</div>
</div>
</template>
<template>
<div>
<!-- 标签页切换:频繁切换,保持组件状态 -->
<div class="tab-content" v-show="activeTab === 'tab1'">标签1内容</div>
<div class="tab-content" v-show="activeTab === 'tab2'">标签2内容</div>
<!-- 展开/收起内容 -->
<div v-show="expanded" class="expandable-content">
详细内容...
</div>
<!-- 移动端菜单切换 -->
<mobile-menu v-show="isMenuOpen" />
</div>
</template>
<script>
export default {
data() {
return {
activeTab: 'tab1', // 可能频繁切换
expanded: false, // 可能频繁展开收起
isMenuOpen: false // 可能频繁打开关闭
}
}
}
</script>
2. 需要保持组件状态
<template>
<div>
<!-- 表单输入:隐藏时保留用户输入 -->
<div v-show="showAdvancedOptions">
<input v-model="advancedOption1" />
<input v-model="advancedOption2" />
<!-- 用户输入的内容不会丢失 -->
</div>
<!-- 视频/音频播放器:隐藏时不中断播放 -->
<video-player v-show="isPictureInPicture" />
</div>
</template>
3. CSS动画/过渡需求
<template>
<div>
<!-- v-show可与transition配合实现平滑显示隐藏 -->
<transition name="fade">
<div v-show="showNotification" class="notification">
通知内容
</div>
</transition>
</div>
</template>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
</style>
<template>
<div>
<button @click="toggle">切换显示 ({{ count }}次)</button>
<!-- v-if: 切换时重新创建/销毁DOM -->
<div v-if="show" class="demo-box">
v-if内容
</div>
<!-- v-show: 仅切换CSS -->
<div v-show="show" class="demo-box">
v-show内容
</div>
</div>
</template>
<script>
export default {
data() {
return {
show: true,
count: 0
}
},
methods: {
toggle() {
this.show = !this.show
this.count++
}
}
}
</script>
v-if减少初始DOM节点数
频繁切换:使用v-show避免DOM操作开销
复杂组件:大组件考虑用v-if避免初始渲染成本
表单内容:需要保留用户输入时用v-show
<template>
<!-- 不推荐:v-for比v-if优先级高,每次循环都会判断 -->
<div v-for="item in list" v-if="item.isActive" :key="item.id">
{{ item.name }}
</div>
<!-- 推荐:先过滤再渲染 -->
<div v-for="item in activeItems" :key="item.id">
{{ item.name }}
</div>
</template>
<script>
export default {
computed: {
activeItems() {
return this.list.filter(item => item.isActive)
}
}
}
</script>
<template> 标签的配合<template>
<div>
<!-- 使用template包裹多个元素 -->
<template v-if="showGroup">
<h3>标题</h3>
<p>描述内容</p>
<button>操作</button>
</template>
<!-- v-show不能直接用在template上 -->
<!-- 但可以这样用: -->
<div v-show="showGroup">
<h3>标题</h3>
<p>描述内容</p>
</div>
</div>
</template>
<template>
<div>
<!-- 当需要频繁切换但保留状态时 -->
<keep-alive>
<component-a v-if="showComponentA" />
<component-b v-else />
</keep-alive>
<!-- 或者使用v-show替代 -->
<component-a v-show="showComponentA" />
<component-b v-show="!showComponentA" />
</div>
</template>
开始
↓
是否需要频繁切换?
├── 是 → 使用 v-show
└── 否 → 继续判断
↓
初始是否需要隐藏?
├── 是 → 使用 v-if
└── 否 → 继续判断
↓
是否需要条件分支?
├── 是 → 使用 v-if (支持v-else)
└── 否 → 任意选择
v-show (如:标签页、折叠面板)
运行时条件很少改变 → v-if (如:权限控制、功能模块)
需要条件分支 → v-if (配合v-else-if/v-else)
初始渲染成本高 → v-if (如:复杂组件、大量数据)
需要保持组件状态 → v-show (如:表单输入、媒体播放)
<template>
<div>
<!-- 正确实践示例 -->
<!-- 场景1:权限控制(不频繁切换) -->
<admin-dashboard v-if="user.role === 'admin'" />
<!-- 场景2:数据加载状态(条件分支) -->
<div v-if="loading">加载中...</div>
<div v-else-if="error">加载失败</div>
<div v-else>显示内容</div>
<!-- 场景3:标签页(频繁切换) -->
<div v-show="activeTab === 'info'">基本信息</div>
<div v-show="activeTab === 'settings'">设置</div>
<div v-show="activeTab === 'history'">历史记录</div>
<!-- 场景4:根据屏幕尺寸显示(条件变化不频繁) -->
<mobile-menu v-if="isMobile" />
<desktop-menu v-else />
</div>
</template>
关键点:理解v-if是"条件渲染",v-show是"条件显示"。根据具体场景的切换频率、初始成本、状态保持需求来做出合适选择。