Skip to content

Vue.js 条件渲染

v-if 指令

v-if 指令用于条件性地渲染元素。当条件为真时,元素会被渲染;为假时,元素不会被渲染到 DOM 中。

基本用法

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

const isVisible = ref(true)
</script>

<template>
  <div>
    <button @click="isVisible = !isVisible">切换显示</button>
    <p v-if="isVisible">这段文字可以被切换显示</p>
  </div>
</template>

v-else

v-else 必须紧跟在 v-ifv-else-if 后面:

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

const awesome = ref(true)
</script>

<template>
  <div>
    <button @click="awesome = !awesome">切换</button>
    <h1 v-if="awesome">Vue 很棒!</h1>
    <h1 v-else>哦不😢</h1>
  </div>
</template>

v-else-if

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

const type = ref('A')
</script>

<template>
  <div>
    <select v-model="type">
      <option>A</option>
      <option>B</option>
      <option>C</option>
      <option>D</option>
    </select>
    
    <div v-if="type === 'A'">类型 A</div>
    <div v-else-if="type === 'B'">类型 B</div>
    <div v-else-if="type === 'C'">类型 C</div>
    <div v-else>其他类型</div>
  </div>
</template>

在 template 上使用 v-if

当需要切换多个元素时,可以在 <template> 上使用 v-if

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

const showDetails = ref(false)
</script>

<template>
  <div>
    <button @click="showDetails = !showDetails">
      {{ showDetails ? '隐藏' : '显示' }}详情
    </button>
    
    <template v-if="showDetails">
      <h3>详细信息</h3>
      <p>这是第一段详情</p>
      <p>这是第二段详情</p>
      <p>这是第三段详情</p>
    </template>
  </div>
</template>

v-show 指令

v-show 也用于条件性地显示元素,但它只是切换元素的 CSS display 属性。

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

const isVisible = ref(true)
</script>

<template>
  <div>
    <button @click="isVisible = !isVisible">切换显示</button>
    <p v-show="isVisible">这段文字使用 v-show 切换</p>
  </div>
</template>

v-if vs v-show

区别

特性v-ifv-show
渲染方式条件性渲染(真正的条件渲染)始终渲染,只切换 display
切换开销较高(销毁和重建)较低(只改变 CSS)
初始渲染开销较低(条件为假时不渲染)较高(始终渲染)
适用场景条件很少改变频繁切换

选择建议

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

const frequentToggle = ref(true)
const rareToggle = ref(true)
</script>

<template>
  <div>
    <!-- 频繁切换,使用 v-show -->
    <button @click="frequentToggle = !frequentToggle">频繁切换</button>
    <p v-show="frequentToggle">使用 v-show(频繁切换)</p>
    
    <!-- 很少改变,使用 v-if -->
    <button @click="rareToggle = !rareToggle">很少切换</button>
    <p v-if="rareToggle">使用 v-if(很少改变)</p>
  </div>
</template>

实战示例:用户登录状态

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

const isLoggedIn = ref(false)
const user = ref({
  name: '张三',
  avatar: 'https://via.placeholder.com/50',
  role: 'admin'
})

function login() {
  isLoggedIn.value = true
}

function logout() {
  isLoggedIn.value = false
}
</script>

<template>
  <div class="header">
    <div v-if="isLoggedIn" class="user-info">
      <img :src="user.avatar" :alt="user.name" class="avatar" />
      <span>欢迎,{{ user.name }}</span>
      <span v-if="user.role === 'admin'" class="badge">管理员</span>
      <button @click="logout">退出</button>
    </div>
    
    <div v-else class="login-prompt">
      <p>您还未登录</p>
      <button @click="login">登录</button>
    </div>
  </div>
</template>

<style scoped>
.header {
  padding: 20px;
  background-color: #f5f5f5;
  border-radius: 8px;
}

.user-info {
  display: flex;
  align-items: center;
  gap: 10px;
}

.avatar {
  width: 50px;
  height: 50px;
  border-radius: 50%;
}

.badge {
  background-color: #42b983;
  color: white;
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 12px;
}

.login-prompt {
  text-align: center;
}

button {
  padding: 8px 16px;
  background-color: #42b983;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

button:hover {
  background-color: #35a372;
}
</style>

实战示例:加载状态

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

const isLoading = ref(true)
const hasError = ref(false)
const data = ref(null)

async function fetchData() {
  isLoading.value = true
  hasError.value = false
  
  try {
    // 模拟 API 调用
    await new Promise(resolve => setTimeout(resolve, 2000))
    data.value = {
      title: '文章标题',
      content: '这是文章内容...'
    }
  } catch (error) {
    hasError.value = true
  } finally {
    isLoading.value = false
  }
}

onMounted(() => {
  fetchData()
})
</script>

<template>
  <div class="container">
    <!-- 加载中 -->
    <div v-if="isLoading" class="loading">
      <div class="spinner"></div>
      <p>加载中...</p>
    </div>
    
    <!-- 错误状态 -->
    <div v-else-if="hasError" class="error">
      <p>❌ 加载失败</p>
      <button @click="fetchData">重试</button>
    </div>
    
    <!-- 成功加载 -->
    <div v-else class="content">
      <h2>{{ data.title }}</h2>
      <p>{{ data.content }}</p>
      <button @click="fetchData">刷新</button>
    </div>
  </div>
</template>

<style scoped>
.container {
  max-width: 600px;
  margin: 20px auto;
  padding: 20px;
  border: 1px solid #ddd;
  border-radius: 8px;
}

.loading {
  text-align: center;
  padding: 40px;
}

.spinner {
  width: 40px;
  height: 40px;
  margin: 0 auto 20px;
  border: 4px solid #f3f3f3;
  border-top: 4px solid #42b983;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.error {
  text-align: center;
  padding: 40px;
  color: #f56c6c;
}

.content {
  padding: 20px;
}

button {
  padding: 8px 16px;
  background-color: #42b983;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  margin-top: 10px;
}

button:hover {
  background-color: #35a372;
}
</style>

实战示例:权限控制

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

const currentUser = ref({
  name: '张三',
  role: 'user', // 'admin', 'user', 'guest'
  permissions: ['read', 'write']
})

const isAdmin = computed(() => currentUser.value.role === 'admin')
const canWrite = computed(() => currentUser.value.permissions.includes('write'))
const canDelete = computed(() => currentUser.value.permissions.includes('delete'))

function changeRole(role) {
  currentUser.value.role = role
  if (role === 'admin') {
    currentUser.value.permissions = ['read', 'write', 'delete']
  } else if (role === 'user') {
    currentUser.value.permissions = ['read', 'write']
  } else {
    currentUser.value.permissions = ['read']
  }
}
</script>

<template>
  <div class="permission-demo">
    <h3>当前用户:{{ currentUser.name }} ({{ currentUser.role }})</h3>
    
    <div class="role-switcher">
      <button @click="changeRole('admin')">切换为管理员</button>
      <button @click="changeRole('user')">切换为普通用户</button>
      <button @click="changeRole('guest')">切换为访客</button>
    </div>
    
    <div class="actions">
      <button>查看(所有人可见)</button>
      
      <button v-if="canWrite">
        编辑(需要写权限)
      </button>
      
      <button v-if="canDelete">
        删除(需要删除权限)
      </button>
      
      <div v-if="isAdmin" class="admin-panel">
        <h4>🔧 管理员面板</h4>
        <p>只有管理员可以看到这个面板</p>
      </div>
    </div>
  </div>
</template>

<style scoped>
.permission-demo {
  max-width: 600px;
  margin: 20px auto;
  padding: 20px;
  border: 1px solid #ddd;
  border-radius: 8px;
}

.role-switcher {
  margin: 20px 0;
  display: flex;
  gap: 10px;
}

.actions {
  margin-top: 20px;
}

.actions button {
  margin-right: 10px;
  margin-bottom: 10px;
  padding: 8px 16px;
  background-color: #42b983;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.admin-panel {
  margin-top: 20px;
  padding: 15px;
  background-color: #fff3cd;
  border: 1px solid #ffc107;
  border-radius: 4px;
}
</style>

总结

  • v-if 是真正的条件渲染,元素会被销毁和重建
  • v-show 只是切换 CSS display 属性
  • v-if 适合条件很少改变的场景
  • v-show 适合频繁切换的场景
  • 可以使用 v-elsev-else-if 创建更复杂的条件逻辑

下一步

接下来学习 列表渲染,了解如何渲染列表数据。