v-if与v-show如何选择?响应式条件渲染的联动机制是什么?

23 阅读15分钟

一、条件渲染的基本模板语法

条件渲染是Vue中根据状态动态控制内容显示的核心能力,它让我们不用手动操作DOM,就能实现“不同状态展示不同内容”的需求。Vue提供了4个核心指令:v-ifv-else-ifv-elsev-show,每个都有明确的适用场景。

1. v-if:动态创建/销毁DOM元素

v-if是最常用的条件渲染指令,它会根据表达式的真假,决定是否将元素插入或移除DOM。例如:

<template>
  <!-- 当isLogin为true时,显示用户信息 -->
  <div v-if="isLogin">
    欢迎回来,{{ username }}!
  </div>
</template>

<script setup>
import { ref } from 'vue'
const isLogin = ref(false) // 初始为未登录状态
const username = ref('')
</script>

isLoginfalse变为true时,div会被创建并插入DOM;反之则被销毁

如果需要同时控制多个元素,但不想添加额外DOM节点(比如<div>),可以用<template>标签包裹——v-if可以直接作用于<template>,渲染后不会留下多余节点:

<template>
  <template v-if="isLogin">
    <h3>用户中心</h3>
    <p>用户名:{{ username }}</p>
    <p>邮箱:{{ email }}</p>
  </template>
</template>

2. v-else与v-else-if:多条件分支

v-else用于添加v-if的“否则”分支,必须紧跟在v-ifv-else-if之后(否则会报错)。v-else-if则用于多条件判断,例如:

<template>
  <div v-if="score >= 90">优秀(≥90)</div>
  <div v-else-if="score >= 80">良好(≥80)</div>
  <div v-else-if="score >= 60">及格(≥60)</div>
  <div v-else>不及格(<60)</div>
</template>

<script setup>
import { ref } from 'vue'
const score = ref(75) // 显示“及格”
</script>

这里的条件会按顺序匹配:如果score是75,会命中v-else-if="score >= 60",显示“及格”。

3. v-show:切换元素的显示状态

v-showv-if功能类似,但不会销毁DOM——它通过切换display: none样式来控制显示。例如:

<template>
  <!-- 屏幕宽度<768时显示移动端菜单 -->
  <button v-show="isMobile">移动端菜单</button>
</template>

<script setup>
import { ref } from 'vue'
const isMobile = ref(window.innerWidth < 768)
</script>

isMobilefalse时,按钮仍存在于DOM中,只是display属性被设为none

4. v-if vs v-show:该选哪个?

很多初学者会混淆这两个指令,我们用一张表总结核心区别:

特性v-ifv-show
DOM操作创建/销毁元素切换display样式
初始渲染成本低(假值时不渲染)高(无论真假都渲染)
切换成本高(需销毁/重建)低(仅切换样式)
适用场景条件不常变化频繁切换的元素

举个例子

  • 登录状态(仅切换1次):用v-if
  • 标签页切换(频繁点击):用v-show

二、响应式数据与条件渲染的联动机制

条件渲染的灵魂是**“响应式数据变化→DOM自动更新”,这背后依赖Vue的响应式系统**。我们需要先理解响应式数据的创建,再看它如何驱动条件渲染。

1. 响应式数据的创建:ref与reactive

Vue3中,响应式数据必须用ref(基本类型,如Boolean/String)或reactive(对象/数组)包裹——它们会“劫持”数据的变化,让Vue能检测到更新。

示例

import { ref, reactive } from 'vue'

// 基本类型:用ref包裹,通过.value访问
const isLogin = ref(false) // 未登录
isLogin.value = true // 修改值,触发更新

// 对象类型:用reactive包裹,直接修改属性
const user = reactive({
  name: '张三',
  role: 'user'
})
user.role = 'admin' // 修改属性,触发更新

2. 联动的核心:依赖追踪与更新

当组件首次渲染时,Vue会收集依赖——即模板中用到的响应式数据(比如v-if="isLogin"中的isLogin)。当响应式数据变化时,Vue会触发依赖更新,重新计算条件表达式,并更新DOM。

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

我们用一个登录状态切换的案例,看完整流程:

案例:登录状态控制
<template>
  <div class="login-page">
    <!-- 未登录:显示登录表单 -->
    <div v-if="!isLogin">
      <input v-model="username" placeholder="用户名" />
      <input v-model="password" type="password" placeholder="密码" />
      <button @click="handleLogin">登录</button>
    </div>
    <!-- 已登录:显示用户信息 -->
    <div v-else>
      欢迎你,{{ username }}!
      <button @click="handleLogout">退出</button>
    </div>
  </div>
</template>

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

// 响应式数据:登录状态、用户名、密码
const isLogin = ref(false)
const username = ref('')
const password = ref('')

// 登录逻辑:模拟接口请求成功后切换状态
const handleLogin = () => {
  if (username.value && password.value) {
    isLogin.value = true // 触发更新!
  }
}

// 退出逻辑:重置状态
const handleLogout = () => {
  isLogin.value = false
  username.value = ''
  password.value = ''
}
</script>

流程拆解(用流程图展示):

graph TD
A[用户点击“登录”] --> B[修改isLogin.value为true]
B --> C[Vue检测到响应式数据变化]
C --> D[触发依赖更新:v-if=&#34;!isLogin&#34;变为false]
D --> E[DOM更新:销毁登录表单,创建用户信息]

3. 优化:用计算属性简化复杂表达式

如果条件表达式很复杂(比如“已登录且是管理员”),直接写在模板里会让代码变乱。这时可以用计算属性把逻辑移到script部分,让模板更简洁。

示例:判断“是否是管理员且已登录”

<template>
  <!-- 用计算属性代替复杂表达式 -->
  <div v-if="isAdminAndLogin">管理员面板</div>
</template>

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

const isLogin = ref(true)
const user = ref({
  role: 'admin' // 管理员角色
})

// 计算属性:缓存结果,只有依赖变化时才重新计算
const isAdminAndLogin = computed(() => {
  return isLogin.value && user.value.role === 'admin'
})
</script>

计算属性的好处:

  • 逻辑复用:多个地方需要判断时,只需写一次;
  • 模板简洁:模板不用写复杂表达式;
  • 缓存优化:只有isLoginuser.role变化时,才重新计算。

三、课后Quiz:巩固所学知识

问题1:v-ifv-show的核心区别是什么?分别适用于什么场景?

答案解析

  • 核心区别:v-if创建/销毁DOMv-show切换display样式
  • 适用场景:
    • v-if:条件不常变化(如登录状态、权限判断);
    • v-show:频繁切换的元素(如标签页、移动端菜单)。

问题2:如何优化模板中复杂的v-if表达式?

答案解析: 用计算属性将逻辑移到script部分。例如:

  • 复杂表达式:v-if="isLogin && user.role === 'admin' && user.status === 'active'"
  • 优化后:用计算属性const isActiveAdmin = computed(() => isLogin.value && user.value.role === 'admin' && user.value.status === 'active'),模板中写v-if="isActiveAdmin"

四、常见报错解决方案

在学习条件渲染时,你可能会遇到以下3类常见错误,我们一一解答:

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

原因v-elsev-else-if没有紧跟在v-ifv-else-if之后(中间插入了其他元素)。
错误代码

<template>
  <div v-if="isLogin">用户信息</div>
  <p>无关内容</p> <!-- 中间插入了p标签 -->
  <div v-else>登录表单</div> <!-- 报错! -->
</template>

解决办法:调整结构,让v-else紧跟v-if

<template>
  <div v-if="isLogin">用户信息</div>
  <div v-else>登录表单</div> <!-- 正确 -->
  <p>无关内容</p>
</template>

预防建议:写条件分支时,先完成v-ifv-else-ifv-else的结构,再添加其他内容。

2. 报错:Cannot read properties of undefined (reading 'value')

原因:未声明响应式数据,或数据不是响应式的。
错误代码

<template>
  <div v-if="isLogin">欢迎回来!</div> <!-- isLogin未声明 -->
</template>

<script setup>
// 忘记导入ref和声明isLogin
// import { ref } from 'vue'
// const isLogin = ref(false)
</script>

解决办法

  1. 导入refreactive
  2. ref/reactive声明响应式数据:
import { ref } from 'vue'
const isLogin = ref(false) // 正确声明

3. 报错:Template compilation error: Unexpected token

原因v-if的表达式语法错误(比如用了赋值运算符=而不是判断运算符===)。
错误代码

<template>
  <div v-if="user.role = 'admin'">管理员</div> <!-- 错误:用了= -->
</template>

解决办法:用===做全等判断:

<div v-if="user.role === 'admin'">管理员</div> <!-- 正确 -->

预防建议:写条件表达式时,避免使用=(赋值),优先用===(全等)或==(相等)。

参考链接