普通视图

发现新文章,点击刷新页面。
昨天以前首页

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

作者 kknone
2025年12月24日 16:27

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

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

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

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

举个例子:


<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


<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。比如:

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

4.2 极少变化?选v-if!

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

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

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

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

举个直观的例子:


<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

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

    <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

<!-- 错误 -->
<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


<li v-for="item in list" v-if="item.isActive">{{ item.name }}</li>

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


<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>标签:


<template v-show="isShow">
    <div>内容</div>
</template>

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

<!-- 正确:用div包裹 -->
<div v-show="isShow">
    <div>内容</div>
</div>

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

8. 参考链接

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

❌
❌