在 Vue 开发中,v-if
和 v-show
都能实现"条件显示",但它们的底层机制、性能表现、适用场景截然不同。
选错一个,可能导致:
- 页面卡顿(频繁切换
v-if
) - 首屏加载变慢(大量
v-show
) - 内存泄漏(组件未销毁)
本文将从 5 个维度 全面对比 v-if
和 v-show
,助你做出最优选择。
一、核心区别速览
对比维度 | v-if |
v-show |
---|---|---|
控制手段 | 动态添加/删除 DOM | 修改 display 样式 |
编译过程 | 局部编译/卸载 | 简单 CSS 切换 |
初始渲染 | 惰性编译(条件为真时才编译) | 无论真假,始终编译 |
切换性能 | ❌ 消耗高(重建组件) | ✅ 消耗低(仅样式) |
初始性能 | ✅ 消耗低(条件为假时跳过) | ❌ 消耗高(始终渲染) |
适用场景 | 条件极少改变 | 频繁切换 |
二、底层原理深度解析
1️⃣ v-if
:DOM 的"销毁与重建"大师
📌 工作流程
vue
<div v-if="isVisible">我是内容</div>
-
✅
isVisible = true
:- 创建 DOM 节点;
- 插入到父元素;
- 触发
mounted
生命周期。
-
❌
isVisible = false
:- 移除 DOM 节点;
- 销毁组件实例;
- 触发
beforeDestroy
→destroyed
。
📌 编译特性:惰性编译
js
data() {
return {
isVisible: false
};
}
- ❌ 初始渲染时,
v-if
内容完全不编译; - ✅ 只有当
isVisible
第一次变为true
时,才开始编译和渲染。
💡 类似"按需加载"。
📌 组件状态
- ❌ 状态不保留:每次切换都会丢失
data
、事件监听器、子组件状态; - ✅ 适合"一次性"或"独立"组件。
2️⃣ v-show
:CSS 的"显示开关"
📌 工作流程
vue
<div v-show="isVisible">我是内容</div>
- ✅
isVisible = true
:display: block
(或原值); - ❌
isVisible = false
:display: none
。
📌 编译特性:始终编译
- ✅ 无论
isVisible
初始值是true
还是false
,内容都会被编译; - ✅ DOM 节点始终存在,只是"看不见";
- ✅ 组件实例、事件监听器、子组件全部保留。
📌 性能表现
- ✅ 切换极快:浏览器对
display
的修改高度优化; - ❌ 占用内存:即使隐藏,组件仍在内存中;
- ✅ 保留状态:非常适合表单、编辑器等需要"记忆"的场景。
三、性能对比实战
📊 场景 1:首屏加载性能
vue
<template>
<!-- 首屏不需要的模块 -->
<div v-if="showSettings">设置面板</div>
<div v-show="showHelp">帮助文档</div>
</template>
方案 | 首屏 DOM 节点数 | 内存占用 | 推荐 |
---|---|---|---|
v-if |
减少 | 低 | ✅ 首屏快 |
v-show |
不变 | 高 | ❌ 拖慢首屏 |
✅ 结论 :首屏隐藏内容,优先用
v-if
。
📊 场景 2:频繁切换的标签页
vue
<template>
<div>
<button @click="activeTab = 'home'">首页</button>
<button @click="activeTab = 'profile'">个人</button>
<Home v-if="activeTab === 'home'" />
<Profile v-if="activeTab === 'profile'" />
<!-- 或 -->
<Home v-show="activeTab === 'home'" />
<Profile v-show="activeTab === 'profile'" />
</div>
</template>
方案 | 切换速度 | 组件状态 | 推荐 |
---|---|---|---|
v-if |
❌ 慢(重建) | ❌ 丢失 | ❌ 不推荐 |
v-show |
✅ 极快 | ✅ 保留 | ✅ 推荐 |
✅ 结论 :频繁切换,用
v-show
。
四、生命周期影响对比
生命周期 | v-if |
v-show |
---|---|---|
created |
每次显示都触发 | 仅首次触发 |
mounted |
每次显示都触发 | 仅首次触发 |
beforeDestroy |
隐藏时触发 | ❌ 不触发 |
destroyed |
隐藏时触发 | ❌ 不触发 |
activated |
❌ 不适用 | ✅ 始终可用 |
💡
v-show
的组件永远不会被销毁 ,因此destroyed
永远不会执行。
五、内存与事件监听器
❌ v-show
的潜在风险
js
export default {
mounted() {
window.addEventListener('resize', this.handleResize);
},
beforeDestroy() {
window.removeEventListener('resize', this.handleResize);
}
}
- ✅
v-if
:隐藏时自动移除事件监听; - ❌
v-show
:即使隐藏,事件监听器依然存在 ,可能导致:- 内存泄漏;
- 多次绑定(如果逻辑不当)。
💡 使用
v-show
时,务必手动清理全局事件。
六、最佳实践指南
✅ 选择决策树
text
你的组件需要频繁切换吗?
├── 是 → 使用 v-show
└── 否
├── 首屏不需要显示?
│ ├── 是 → 使用 v-if(减少首屏负载)
│ └── 否 → 两者皆可,推荐 v-show(避免重复渲染)
└── 是否包含重型组件(如图表、编辑器)?
├── 是 → v-if(避免初始化开销)
└── 否 → v-show
✅ 推荐组合使用
vue
<!-- 首次加载用 v-if,之后用 v-show -->
<div v-if="loaded" v-show="isVisible">
重型内容
</div>
js
data() {
return {
loaded: false,
isVisible: false
};
},
mounted() {
// 数据加载完成后标记 loaded
api.getData().then(() => {
this.loaded = true;
this.isVisible = true;
});
}
💡 结语
"v-if 是'开关',v-show 是'窗帘'。"
- ✅
v-if
:适合条件稳定 、首屏隐藏 、重型组件; - ✅
v-show
:适合频繁切换 、轻量组件 、需保留状态。
记住:
"少用
v-if
切换,多用v-show
显示。"
掌握这个原则,你的 Vue 应用将更加流畅、高效、稳定。