05Vue 3 条件渲染详解

一、核心概念

条件渲染是指根据特定条件动态控制 DOM 元素的显示与隐藏。Vue 3 提供了多种方式来实现条件渲染。

二、主要指令

1. v-if / v-else-if / v-else

基本用法:

<template>
  <div>
    <!-- v-if -->
    <p v-if="isVisible">这段文字会条件显示</p>
    
    <!-- v-else-if 和 v-else -->
    <div v-if="type === 'A'">显示 A</div>
    <div v-else-if="type === 'B'">显示 B</div>
    <div v-else>显示其他</div>
  </div>
</template>

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

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

特点:

  • 真正的条件渲染:元素在条件为假时不会被渲染到 DOM 中

  • 切换开销较大:每次切换都会创建/销毁元素和组件

  • 适用于不频繁切换的场景

2. v-show

<template>
  <div>
    <p v-show="isVisible">这段文字会条件显示/隐藏</p>
  </div>
</template>

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

const isVisible = ref(true)
</script>

特点:

  • 通过 CSS 的 display: none 控制显示/隐藏

  • 元素始终会被渲染并保留在 DOM 中

  • 切换开销较小

  • 适用于频繁切换的场景

三、v-if vs v-show 对比

微信图片_20260210135052_391_29

四、高级用法

1. 在 <template> 上使用 v-if

<template>
  <!-- 包裹多个元素 -->
  <template v-if="showSection">
    <h2>标题</h2>
    <p>内容...</p>
    <p>更多内容...</p>
  </template>
</template>

2. 条件渲染组合示例

<template>
  <div>
    <!-- 登录状态判断 -->
    <div v-if="isLoading">加载中...</div>
    <div v-else-if="error">加载失败: {{ error }}</div>
    <div v-else-if="data.length === 0">暂无数据</div>
    <ul v-else>
      <li v-for="item in data" :key="item.id">{{ item.name }}</li>
    </ul>
    
    <!-- 根据用户角色显示不同内容 -->
    <div v-if="user.role === 'admin'">
      <button>管理设置</button>
      <button>删除用户</button>
    </div>
    <div v-else-if="user.role === 'editor'">
      <button>编辑内容</button>
    </div>
    <div v-else>
      <button>查看内容</button>
    </div>
    
    <!-- 结合计算属性 -->
    <div v-if="shouldShowAdvancedOptions">
      高级选项...
    </div>
  </div>
</template>

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

const isLoading = ref(false)
const error = ref(null)
const data = ref([])
const user = ref({ role: 'admin' })

// 使用计算属性处理复杂条件
const shouldShowAdvancedOptions = computed(() => {
  return user.value.role === 'admin' && data.value.length > 10
})
</script>

3. 与 v-for 一起使用

<template>
  <ul>
    <!-- 不推荐:v-if 和 v-for 在同一元素 -->
    <!-- <li v-for="item in items" v-if="item.isActive"> {{ item.name }} </li> -->
    
    <!-- 推荐:使用 template 包裹 -->
    <template v-for="item in items" :key="item.id">
      <li v-if="item.isActive">{{ item.name }}</li>
    </template>
    
    <!-- 或者使用计算属性过滤 -->
    <li v-for="item in activeItems" :key="item.id">{{ item.name }}</li>
  </ul>
</template>

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

const items = ref([
  { id: 1, name: 'Item 1', isActive: true },
  { id: 2, name: 'Item 2', isActive: false },
  { id: 3, name: 'Item 3', isActive: true }
])

// 使用计算属性过滤
const activeItems = computed(() => items.value.filter(item => item.isActive))
</script>

五、性能优化技巧

1. 使用 key 管理可复用元素

<template>
  <div>
    <!-- 添加 key 强制重新渲染 -->
    <div v-if="loginType === 'username'" key="username">
      <label>用户名</label>
      <input placeholder="请输入用户名">
    </div>
    <div v-else key="email">
      <label>邮箱</label>
      <input placeholder="请输入邮箱">
    </div>
    <button @click="toggleLoginType">切换登录方式</button>
  </div>
</template>

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

const loginType = ref('username')

const toggleLoginType = () => {
  loginType.value = loginType.value === 'username' ? 'email' : 'username'
}
</script>

2. 懒加载组件

<template>
  <div>
    <button @click="showComponent = !showComponent">切换组件</button>
    
    <!-- 使用 v-if 实现懒加载 -->
    <UserProfile v-if="showComponent" />
    
    <!-- 或者使用 Suspense + defineAsyncComponent -->
    <Suspense>
      <template #default>
        <AsyncComponent v-if="showComponent" />
      </template>
      <template #fallback>
        <div>加载中...</div>
      </template>
    </Suspense>
  </div>
</template>

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

const showComponent = ref(false)

// 异步组件
const AsyncComponent = defineAsyncComponent(() =>
  import('./AsyncComponent.vue')
)
</script>

六、实际应用示例

<template>
  <div class="user-dashboard">
    <!-- 权限控制 -->
    <AdminPanel v-if="user.role === 'admin'" />
    
    <!-- 功能开关 -->
    <BetaFeatures v-show="features.betaEnabled" />
    
    <!-- 空状态处理 -->
    <div v-if="tasks.length === 0" class="empty-state">
      <p>还没有任务,快去创建一个吧!</p>
      <button @click="createTask">创建任务</button>
    </div>
    
    <!-- 骨架屏 -->
    <div v-else-if="loading" class="skeleton">
      <div class="skeleton-item"></div>
      <div class="skeleton-item"></div>
      <div class="skeleton-item"></div>
    </div>
    
    <!-- 正常显示 -->
    <TaskList v-else :tasks="tasks" />
    
    <!-- 响应式显示 -->
    <MobileMenu v-show="isMobileScreen" />
    <DesktopMenu v-show="!isMobileScreen" />
  </div>
</template>

<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
import AdminPanel from './AdminPanel.vue'
import BetaFeatures from './BetaFeatures.vue'
import TaskList from './TaskList.vue'
import MobileMenu from './MobileMenu.vue'
import DesktopMenu from './DesktopMenu.vue'

const user = ref({ role: 'user' })
const features = ref({ betaEnabled: true })
const tasks = ref([])
const loading = ref(true)
const windowWidth = ref(window.innerWidth)

const isMobileScreen = computed(() => windowWidth.value < 768)

const updateWindowWidth = () => {
  windowWidth.value = window.innerWidth
}

onMounted(() => {
  window.addEventListener('resize', updateWindowWidth)
  fetchTasks()
})

onUnmounted(() => {
  window.removeEventListener('resize', updateWindowWidth)
})

const fetchTasks = async () => {
  loading.value = true
  try {
    // 模拟 API 调用
    await new Promise(resolve => setTimeout(resolve, 1000))
    tasks.value = [
      { id: 1, title: '学习 Vue 3', completed: false },
      { id: 2, title: '完成项目', completed: true }
    ]
  } finally {
    loading.value = false
  }
}

const createTask = () => {
  // 创建任务逻辑
}
</script>

<style scoped>
.empty-state {
  text-align: center;
  padding: 2rem;
}

.skeleton-item {
  height: 20px;
  background: #eee;
  margin: 10px 0;
  border-radius: 4px;
  animation: pulse 1.5s infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}
</style>

七、最佳实践

  1. 选择合适的指令:

    • 使用 v-if 当元素不频繁切换或初始条件为假

    • 使用 v-show 当元素需要频繁切换显示状态

  2. 避免 v-if 和 v-for 同时使用:

    • 优先使用计算属性过滤数据

    • 或使用 <template> 包裹

  3. 复杂条件使用计算属性:

    • 提高模板可读性

    • 便于复用和维护

  4. 考虑可访问性:

    • 使用 v-show 时注意屏幕阅读器的行为

    • 确保隐藏内容不会影响可访问性

  5. 性能监控:

    • 大量 v-if 切换时注意性能影响

    • 必要时使用虚拟滚动或分页

通过合理使用条件渲染,可以创建更加动态、高效和用户友好的 Vue 应用。

posted @ 2026-02-10 14:31  麻辣~香锅  阅读(7)  评论(0)    收藏  举报