Vue3 v-if与v-show:销毁还是隐藏,如何抉择?

1. v-if 与 v-show 的基本概念

条件渲染是Vue3中控制界面显示的核心能力,而v-ifv-show是实现这一能力的两大核心指令。它们的本质差异,要从"**是否真正改变组件的存在 **"说起。

1.1 v-if:真正的"存在与否"

v-if是"破坏性条件渲染 "------当条件为true时,它会创建 组件实例并渲染到DOM中;当条件为false时,它会销毁 组件实例并从DOM中移除。换句话说,v-if控制的是"组件是否存在"。

举个例子:

html 复制代码
<button @click="toggle">切换文本</button>
<div v-if="isShow">Hello, Vue3!</div>

isShowfalse时,你在浏览器DevTools里找不到这个div ------它被完全销毁了。而且,组件的生命周期钩子(如onMounted/ onUnmounted)会随着条件切换触发:销毁时执行onUnmounted,重建时执行onMounted

1.2 v-show:只是"看得见看不见"

v-show是"非破坏性条件渲染 "------无论条件真假,它都会先把组件渲染到DOM中,再通过修改CSS的display属性 控制可见性。换句话说, v-show控制的是"组件是否可见",但组件始终存在。

同样的例子,换成v-show

html 复制代码
<button @click="toggle">切换文本</button>
<div v-show="isShow">Hello, Vue3!</div>

isShowfalse时,div依然在DOM中,只是多了style="display: none"。此时,组件实例没有被销毁,生命周期钩子也不会触发------它只是"被藏起来了"。

2. 原理拆解:为什么行为差异这么大?

理解原理是选择的关键,我们用"生活比喻"帮你快速记住:

  • v-if像"客房的家具":客人来了(条件为真),你把家具搬出来(创建组件);客人走了(条件为假),你把家具收起来(销毁组件)。每次搬运都要花时间(切换成本高),但平时不占空间(初始化成本低)。
  • v-show 像"客厅的电视":不管你看不看(条件真假),电视都在那里(存在于DOM);你只是用遥控器(v-show )切换"显示/隐藏"(修改CSS)。切换动作很快(成本低),但始终占地方(初始化成本高)。

3. 性能对比:初始化 vs 切换成本

v-ifv-show的性能差异,本质是**"空间换时间"还是"时间换空间"**的选择:

3.1 初始化成本:v-if 更"省空间"

当初始条件为false时:

  • v-if:不渲染任何内容,DOM中无节点,初始化速度快
  • v-show:强制渲染组件,DOM中存在节点,初始化速度慢

比如,一个"仅管理员可见"的按钮,用v-if更合适------普通用户打开页面时,按钮不会被渲染,减少页面加载时间。

3.2 切换成本:v-show 更"省时间"

当条件需要频繁切换时:

  • v-if:每次切换都要销毁重建组件,涉及DOM操作和生命周期钩子,切换速度慢
  • v-show:仅修改CSS属性,无DOM重建,切换速度快

比如, tabs 切换、弹窗显示隐藏,用v-show更流畅------用户点击时不会有延迟。

4. 选择策略:到底该用谁?

结合原理和性能,我们总结了3条黄金法则

4.1 频繁切换?选v-show!

如果组件需要反复显示/隐藏(如 tabs、弹窗、折叠面板),优先用v-show。比如:

html 复制代码
<!-- 频繁切换的弹窗,用v-show -->
<modal v-show="isModalOpen" @close="isModalOpen = false"></modal>

4.2 极少变化?选v-if!

如果条件几乎不会改变(如权限控制、初始化提示),优先用v-if。比如:

html 复制代码
<!-- 仅管理员可见的按钮,用v-if -->
<button v-if="isAdmin" @click="deleteItem">删除</button>

4.3 要保留状态?选v-show!

如果组件包含需要保留的状态(如表单输入、播放器进度),必须用v-show------v-if会销毁组件,导致状态丢失。

举个直观的例子:

html 复制代码
<template>
    <button @click="toggle">切换输入框</button>
    <!-- v-if:输入内容会重置 -->
    <div v-if="isShow">
        <input type="text" placeholder="v-if 输入框"/>
    </div>
    <!-- v-show:输入内容保留 -->
    <div v-show="isShow">
        <input type="text" placeholder="v-show 输入框"/>
    </div>
</template>

<script setup>
    import {ref} from 'vue'

    const isShow = ref(true)
    const toggle = () => isShow.value = !isShow.value
</script>

往期文章归档

试着输入内容后切换:v-if的输入框会清空(组件销毁),v-show的输入框内容不变(组件存在)。

5. 动手实践:看得到的差异

为了更直观,我们用生命周期钩子验证两者的区别:

  1. 创建子组件Child.vue

    html 复制代码
    <template><div>我是子组件</div></template>
    <script setup>
    import { onMounted, onUnmounted } from 'vue'
    onMounted(() => console.log('子组件挂载了!'))
    onUnmounted(() => console.log('子组件销毁了!'))
    </script>
  2. 父组件中切换:

    html 复制代码
    <template>
      <button @click="toggle">切换子组件</button>
      <!-- 用v-if时,切换会打印日志 -->
      <Child v-if="isShow" />
      <!-- 用v-show时,切换无日志 -->
      <!-- <Child v-show="isShow" /> -->
    </template>
    
    <script setup>
    import { ref } from 'vue'
    import Child from './Child.vue'
    const isShow = ref(true)
    const toggle = () => isShow.value = !isShow.value
    </script>

运行后点击按钮:

  • v-if:切换会打印"子组件销毁了!"和"子组件挂载了!"(组件生死轮回);
  • v-show:无日志(组件始终存在)。

6. 课后Quiz:巩固你的理解

问题 :你在开发"用户设置"页面,其中"高级设置"面板可以点击"展开/收起"切换。面板包含多个输入框(如"个性签名"),需要保留用户输入。请问该用 v-if还是v-show?为什么?

答案解析

v-show。原因有二:

  1. 频繁切换 :用户可能多次展开/收起,v-show切换成本更低;
  2. 状态保留 :输入框需要保留内容,v-show不会销毁组件,状态不会丢失。

7. 常见报错与解决

使用v-if/v-show时,这些"坑"要避开:

问题1:v-show 不能和 v-else 一起用

报错v-else can only be used with v-if
原因v-elsev-if的配套指令,v-show是CSS控制,无法配合。
解决 :用v-if代替v-show,或分开写v-show

html 复制代码
<!-- 错误 -->
<div v-show="isShow">内容A</div>
<div v-else>内容B</div>

<!-- 正确:用v-if -->
<div v-if="isShow">内容A</div>
<div v-else>内容B</div>

<!-- 正确:分开写v-show -->
<div v-show="isShow">内容A</div>
<div v-show="!isShow">内容B</div>

问题2:v-if 和 v-for 一起用导致性能低

报错场景 :同一个元素同时用v-ifv-for

html 复制代码
<li v-for="item in list" v-if="item.isActive">{{ item.name }}</li>

原因 :Vue3中v-for优先级高于v-if,会先循环所有元素,再逐个判断条件,重复计算导致性能差。
解决 :用computed先过滤数组:

html 复制代码
<template>
    <li v-for="item in activeItems" :key="item.id">{{ item.name }}</li>
</template>

<script setup>
    import {ref, computed} from 'vue'

    const list = ref([/* 数据 */])
    // 先过滤出active的item
    const activeItems = computed(() => list.value.filter(item => item.isActive))
</script>

问题3:v-show 对 template 无效

报错场景 :用v-show控制<template>标签:

html 复制代码
<template v-show="isShow">
    <div>内容</div>
</template>

原因<template>是Vue的虚拟标签,不会渲染成真实DOM,v-show无法修改其display属性。
解决 :用真实DOM元素(如<div>)包裹,或用<template v-if>

html 复制代码
<!-- 正确:用div包裹 -->
<div v-show="isShow">
    <div>内容</div>
</div>

<!-- 正确:用v-if -->
<template v-if="isShow">
    <div>内容</div>
</template>

8. 参考链接

参考链接:vuejs.org/guide/essen...

相关推荐
重铸码农荣光2 小时前
🎯 从零搭建一个 React Todo 应用:父子通信、状态管理与本地持久化全解析!
前端·react.js·架构
Mr_chiu2 小时前
🚀 效率暴增!Vue.js开发必知的15个神级提效工具
前端
JimmyWhat2 小时前
Vue单页应用路由404问题:服务器配置与Hash模式解决方案
vue.js
shanLion2 小时前
Vite项目中process报红问题的深层原因与解决方案
前端·javascript
黄俊懿2 小时前
【深入理解SpringCloud微服务】Seata(AT模式)源码解析——全局事务的回滚
java·后端·spring·spring cloud·微服务·架构·架构师
烟袅2 小时前
从零构建一个待办事项应用:一次关于组件化与状态管理的深度思考
前端·javascript·react.js
前端小万2 小时前
草稿
前端
Java编程爱好者2 小时前
SpringBoot启动太慢?几个优化技巧
后端
喷火龙8号2 小时前
修复 Hertz + OpenTelemetry 链路追踪中的数据竞争问题
后端