Vue.js: 条件渲染与列表渲染

最后更新:2026-10-20

条件渲染(v-if / v-show)和列表渲染(v-for)是 Vue 模板最常用的两个指令。前者控制"显示不显示",后者控制"显示多少个"。两者组合能渲染 90% 的 UI 场景。

掌握 v-if 和 v-for 的 5 大区别,以及 :key 的正确用法,是写出高性能 Vue 应用的关键。

相关指令:v-else-if v-show v-else v-for v-if

1. 你将学到


2. 一个仪表盘"if 套娃"的代码灾难

(1) 痛点:5 个 v-if 嵌套,调试地狱

Alice 的仪表盘代码失控了:

Charlie 的代码审查意见:

"Alice,这根本没法读。5 层 v-if 嵌套。再加 2 个条件怎么办?我们需要更清晰的模式。"

(2) Vue 解法:v-if 扁平化 + 计算属性

(3) 收益


3. v-if / v-else-if / v-else 条件渲染

(1) 基本语法

(2) template 元素

(3) 5 大使用场景

场景 用 v-if
登录/未登录 ✅
加载中/加载完成 ✅
空数据/有数据 ✅
错误状态 ✅
频繁切换显示/隐藏 ❌(用 v-show)

▶ 示例:5 种 v-if/v-show 写法

▶ 试一试

输出:

TEXT
1. v-if 显示(点击切换)
2. v-show 显示
3. 类型 A
4. Admin Panel + Edit 按钮        ← 5a 不渲染,只显示 5b
点"切换 show" → 第 1、2 行消失;点"切换 type" → 第 3 行变"3. 类型 B"

4. v-show vs v-if 5 大区别

(1) 核心区别

(2) 5 大对比

维度 v-if v-show
DOM 操作 条件 false 时移除元素 始终在 DOM,只切 display
切换性能 差(每次销毁/重建) 好(只切 CSS)
初始渲染 条件 false 时不渲染 始终渲染(即使不可见)
配合 transition ✅ 完美(动画进出) ❌ 不支持
适用场景 偶尔切换 频繁切换

(3) 选择建议

JS
// 偶尔切换(如登录/未登录)→ v-if
<div v-if="isLoggedIn">Welcome, {{ user.name }}</div>
<div v-else>Please login</div>

// 频繁切换(如 tab 切换)→ v-show
<button v-show="isActive">Active</button>
<button v-show="!isActive">Inactive</button>

5. v-for 列表渲染

(1) 遍历数组

(2) 遍历对象

(3) 遍历数字

(4) 遍历字符串

▶ 示例:5 种 v-for 写法

▶ 试一试

输出:

TEXT
1. 数组 → Apple / Banana / Cherry
2. 数组 + 索引 → 1. Apple / 2. Banana / 3. Cherry
3. 对象 → name: Alice / age: 25 / email: alice@example.com
4. 数字 → [1][2][3][4][5]
5. 字符串 → H e l l o

▶ 示例:v-if 和 v-for 优先级

▶ 试一试

输出:

TEXT
列表只有 Alice(Bob 的 isActive 为 false,被 computed 过滤掉)
点"切换 Alice 状态" → 列表清空
再点一次 → Alice 重新出现在列表里
⚠️ Vue 3 中 v-if 优先级高于 v-for(Vue 2 相反)。官方推荐:不要一起用,用 template 分开或 computed 过滤。


6. :key 的作用和最佳实践

(1) :key 是什么?

:key 给每个 v-for 项一个唯一标识,帮助 Vue 高效更新 DOM。

(2) 为什么需要 :key?

100%
graph LR
    A[原列表 A B C] --> B[新列表 B C D]
    B --> C[没有 key:可能错位]
    B --> D[有 key:正确更新]
    
    style C fill:#ff6b6b
    style D fill:#42b883

没有 :key 时:

有 :key 时:

(3) :key 最佳实践

VUE
<!-- ✅ 推荐:唯一 ID -->
<li v-for="user in users" :key="user.id">

<!-- ✅ 次选:唯一字段(邮箱、手机号等)-->
<li v-for="user in users" :key="user.email">

<!-- ❌ 避免:index(数据变化时会错位)-->
<li v-for="(user, index) in users" :key="index">

<!-- ❌ 绝对不要:random()(每次渲染都不同,强制重建)-->
<li v-for="user in users" :key="Math.random()">

▶ 示例:5 种 :key 用法对比

▶ 试一试

输出:

TEXT
1、2 两段都渲染 Alice / Bob
3. 复合 key → Post A / Post B
4. index 当 key → X / Y
5. random() 当 key → X / Y 但每次渲染 key 都变,DOM 被整体重建

7. 列表过滤 / 排序 / 分页

(1) 过滤 + 排序(计算属性)

JS
const { ref, computed } = Vue

const items = ref([
  { id: 1, name: 'iPhone', price: 999, category: 'phone' },
  { id: 2, name: 'MacBook', price: 2499, category: 'laptop' },
  { id: 3, name: 'iPad', price: 599, category: 'tablet' }
])

const searchQuery = ref('')
const selectedCategory = ref('all')
const sortBy = ref('price')  // 'price' / 'name'

// 过滤 + 排序(computed 自动缓存)
const filteredItems = computed(() => {
  let result = items.value
  
  // 1. 按搜索词过滤
  if (searchQuery.value) {
    result = result.filter(item =>
      item.name.toLowerCase().includes(searchQuery.value.toLowerCase())
    )
  }
  
  // 2. 按分类过滤
  if (selectedCategory.value !== 'all') {
    result = result.filter(item => item.category === selectedCategory.value)
  }
  
  // 3. 排序
  result = [...result].sort((a, b) => {
    if (sortBy.value === 'price') return a.price - b.price
    if (sortBy.value === 'name') return a.name.localeCompare(b.name)
    return 0
  })
  
  return result
})

(2) 分页

JS
const currentPage = ref(1)
const pageSize = ref(10)

// 分页后的数据
const paginatedItems = computed(() => {
  const start = (currentPage.value - 1) * pageSize.value
  return filteredItems.value.slice(start, start + pageSize.value)
})

// 总页数
const totalPages = computed(() =>
  Math.ceil(filteredItems.value.length / pageSize.value)
)

8. 完整示例:动态商品列表

▶ 示例:列表过滤 + 排序 + 分页完整示例

▶ 展开运行示例(67 行)
▶ 试一试

输出:

TEXT
搜索框为空,分类选"全部",排序选"价格"
列表按价格升序:iPad - $599 / iPhone - $999 / MacBook - $2499
页码显示 1 / 1,"上一页""下一页"都置灰不可点
输入 "Mac" → 列表只剩 MacBook - $2499,页码仍 1 / 1

(1) 📊 5 个常见错误

错误 现象 解决
没有 :key 列表错位、性能差 加 :key="item.id"
v-for 和 v-if 一起用 v-if 优先级混乱 用 template 包装或 computed
用 index 当 key 排序时错位 用唯一 ID
v-show 不支持 <transition> 不能动画 改用 v-if + <transition>
列表太大(10万+) 页面卡死 用 vue-virtual-scroller 虚拟滚动

相关指令:v-else-if v-show v-else v-for v-if

❓ 常见问题

Q v-if 和 v-show 怎么选?
A 偶尔切换用 v-if(节省初始渲染),频繁切换用 v-show(避免反复销毁/重建 DOM)。登录/未登录用 v-if,tab 切换用 v-show。
Q v-for 必须用 :key 吗?
A 强烈推荐用 :key 避免性能问题和组件状态错位。用唯一 ID(item.id)最佳,index 次之,绝对不要用 random()。
Q v-if 和 v-for 一起用,优先级?
A Vue 3 中 v-if 优先级高于 v-for(Vue 2 相反)。但官方推荐不要一起用,用 template 分开或用 computed 过滤。
Q template 元素有什么用?
A template 是 Vue 逻辑包装元素,不渲染到 DOM。用于 v-if/v-for 包含多个元素时,避免多余的 div。
Q 列表性能差怎么办?
A 3 个优化:(1) 加 :key;(2) 用 computed 缓存过滤/排序结果;(3) 大列表(10万+)用 vue-virtual-scroller 虚拟滚动,只渲染可见区域。
Q v-for 遍历对象能拿到 key/value 吗?
A 能。v-for="(value, key, index) in obj",参数顺序:值、键、索引。
Q 怎么实现"无数据时显示提示"?
A 用 v-if/v-else 组合:<div v-if="items.length">列表</div><div v-else>无数据</div>。
Q 列表点击项高亮怎么实现?
A 给每个项加 activeIndex 比较:@click="activeIndex = item.id" :class="{ active: activeIndex === item.id }"。

📖 小节


📝 作业

  1. 基础题(难度⭐) 用 v-if/v-else 实现登录/未登录切换:

    • 已登录:显示用户名和"登出"按钮
    • 未登录:显示"请登录"和登录表单
    • 用 template 包装多元素
  2. 进阶题(难度⭐⭐) 用 v-for 实现 Todo 列表:

    • 3 个状态:全部 / 未完成 / 已完成(3 个按钮切换)
    • 列表显示对应状态的任务
    • 每项有"完成"和"删除"按钮
    • 底部显示"未完成:N 项"
  3. 挑战题(难度⭐⭐⭐) 实现完整的商品筛选 + 排序 + 分页系统(5 个文件):

    1. 商品数据:30 个,含 name/price/category/createdAt
    2. 搜索:按 name 模糊搜索
    3. 分类筛选:5 个分类(all/phone/laptop/tablet/headphone)
    4. 排序:价格升序/降序/名称/最新
    5. 分页:10 项/页,显示页码导航
    6. 性能:computed 缓存过滤/排序结果
    7. 大列表优化:1000 项测试,确保不卡
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏