普通视图

发现新文章,点击刷新页面。
昨天 — 2025年12月24日首页

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…

昨天以前首页

Vue3条件渲染中v-if系列指令如何合理使用与规避错误?

作者 kknone
2025年12月20日 16:13

一、Vue3条件渲染的核心概念

在Vue3中,条件渲染是指根据响应式数据的真假,决定是否在页面上渲染某个元素或组件。而v-ifv-elsev-else-if 这组指令,就是实现条件渲染的“核心工具”——它们像一套“逻辑开关”,帮你精准控制DOM的显示与隐藏。

二、v-if系列指令的语法与用法

我们先逐个拆解每个指令的作用,再通过实际案例串起来用。

2.1 v-if:基础条件判断

v-if是最基础的条件指令,它的语法很简单:


<元素 v-if="条件表达式">要渲染的内容</元素>
  • 条件表达式:可以是任何返回truefalse的JavaScript表达式(比如isLoginscore >= 90)。
  • 惰性渲染v-if是“懒”的——如果初始条件不满足(比如isLogin = false),元素不会被渲染到DOM中(连DOM节点都不会生成);只有当条件变为 true时,才会“从零开始”创建元素及其子组件。

举个例子:判断用户是否登录,显示不同的内容:


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

  const isLogin = ref(false) // 初始未登录
</script>

<template>
  <div>
    <!-- 登录后显示 -->
    <p v-if="isLogin">欢迎回来,用户!</p>
    <!-- 未登录显示 -->
    <button v-if="!isLogin" @click="isLogin = true">点击登录</button>
  </div>
</template>

当点击按钮时,isLogin变为true,未登录的按钮会被销毁,同时渲染“欢迎”文本——这就是v-if的“销毁/重建”逻辑。

往期文章归档
免费好用的热门在线工具

2.2 v-else:补充默认分支

如果v-if的条件不满足,你可以用v-else添加一个“默认选项”。但要注意:
v-else必须紧跟在v-ifv-else-if的后面,中间不能有其他兄弟元素(否则Vue无法识别它属于哪个条件)。

修改上面的例子,用v-else简化未登录的情况:


<template>
    <div>
        <p v-if="isLogin">欢迎回来,用户!</p>
        <!-- 直接跟在v-if后面,无需写条件 -->
        <button v-else @click="isLogin = true">点击登录</button>
    </div>
</template>

2.3 v-else-if:多分支条件判断

当需要判断多个条件时,用v-else-if连接。它的语法是:


<元素 v-if="条件1">内容1</元素>
<元素 v-else-if="条件2">内容2</元素>
<元素 v-else-if="条件3">内容3</元素>
<元素 v-else>默认内容</元素>

关键规则:Vue会按指令的顺序依次判断,满足第一个条件就停止(所以条件的顺序很重要!)。

比如根据分数显示等级(最常见的多分支场景):


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

  const score = ref(85) // 响应式分数,初始85分
</script>

<template>
  <div class="score-level">
    <h3>你的分数:{{ score }}</h3>
    <!-- 顺序:从高到低 -->
    <p v-if="score >= 90" class="excellent">等级:优秀(≥90)</p>
    <p v-else-if="score >= 80" class="good">等级:良好(80-89)</p>
    <p v-else-if="score >= 60" class="pass">等级:及格(60-79)</p>
    <p v-else class="fail">等级:不及格(<60)</p>
  </div>
</template>

<style scoped>
  .excellent {
    color: #4CAF50;
  }

  .good {
    color: #2196F3;
  }

  .pass {
    color: #FFC107;
  }

  .fail {
    color: #F44336;
  }
</style>

运行这个组件,修改score的值(比如改成9550),会看到等级自动切换——这就是多分支条件渲染的实际效果。

三、条件渲染的流程逻辑(附流程图)

为了更直观理解v-if系列的执行顺序,我们画一个判断流程图

flowchart TD
    A[开始] --> B{检查v-if条件}
    B -->|满足| C[渲染v-if内容,结束]
    B -->|不满足| D{检查下一个v-else-if条件}
    D -->|满足| E[渲染对应内容,结束]
    D -->|不满足| F{还有v-else-if吗?}
    F -->|是| D
    F -->|否| G[渲染v-else内容,结束]

简单来说:按顺序“闯关”,满足条件就“通关”,否则继续,直到最后一个v-else

四、课后Quiz:巩固你的理解

Quiz 1:v-if和v-show有什么区别?

问题:同样是“隐藏元素”,v-ifv-show的核心差异是什么?分别适合什么场景?

答案解析(参考Vue官网):

  • v-if销毁/重建DOM——条件不满足时,元素从DOM中消失;条件满足时,重新创建。适合条件很少变化的场景(比如权限判断),因为初始渲染更省性能。
  • v-show修改CSS显示——无论条件如何,元素都会渲染到DOM,只是用display: none隐藏。适合条件频繁切换的场景(比如 tabs 切换),因为切换时无需销毁重建,更高效。

Quiz 2:为什么v-else必须紧跟v-if?

问题:如果写v-if之后隔了一个div再写v-else,会报错吗?为什么?

答案解析
会报错!错误信息是“v-else has no adjacent v-if”。
原因:Vue需要明确v-else对应的“上级条件”——它必须与最近的v-if/v-else-if直接相邻(中间不能有其他兄弟元素)。如果隔开,Vue无法识别两者的关联。

五、常见报错与解决方案

在使用v-if系列时,新手常遇到以下问题,我们逐一解决:

1. 报错:“v-else/v-else-if has no adjacent v-if”

  • 原因v-elsev-else-if没有紧跟对应的v-if(中间有其他元素)。
    比如:
    <div v-if="isShow"></div>
    <p>无关内容</p> <!-- 中间的p元素导致错误 -->
    <div v-else></div>
    
  • 解决:删除中间的无关元素,让v-else直接紧跟v-if
  • 预防:写v-else前,先检查前面的元素是否是v-ifv-else-if

2. 报错:“条件变化时,v-if内容不更新”

  • 原因:条件变量不是响应式的(比如用let isShow = false而不是ref(false)),Vue无法追踪其变化。
  • 解决:用ref(基本类型)或reactive(对象/数组)包裹条件变量:
    // 错误写法
    let isShow = false 
    // 正确写法
    const isShow = ref(false)
    
  • 预防:所有需要“随数据变化而更新”的变量,都用Vue的响应式API(ref/reactive)定义。

3. 逻辑错误:v-else-if顺序导致条件失效

  • 例子:先写v-else-if="score >= 60",再写v-else-if="score >= 80"——此时80分会被第一个条件拦截,永远到不了第二个。
  • 原因:Vue按指令顺序判断,满足第一个条件就停止。
  • 解决:将更严格的条件放在前面(比如先>=90,再>=80,最后>=60)。

参考链接

Vue官网条件渲染文档:vuejs.org/guide/essen…

❌
❌