跳到主要内容

第 5 节:条件渲染与列表渲染

📚 本节目标

  • 掌握 v-if、v-else-if、v-else 的使用
  • 理解 v-if 和 v-show 的区别
  • 掌握 v-for 循环渲染列表
  • 理解 key 属性的作用

一、条件渲染:v-if / v-else

什么是条件渲染?

大白话:根据条件决定某个元素显示还是隐藏。

就像日常生活:

  • 如果下雨 → 带伞
  • 如果不下雨 → 不带伞

在 Vue 中:

  • 如果条件为真 → 显示元素
  • 如果条件为假 → 不显示元素

基本用法

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>v-if 基础示例</title>
<style>
.container {
max-width: 600px;
margin: 50px auto;
padding: 20px;
}
.message {
padding: 15px;
border-radius: 5px;
margin: 10px 0;
}
.success {
background: #d4edda;
color: #155724;
}
.error {
background: #f8d7da;
color: #721c24;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<h2>登录状态</h2>

<button @click="isLoggedIn = !isLoggedIn">
{{ isLoggedIn ? '退出登录' : '登录' }}
</button>

<!-- v-if:条件为 true 时显示 -->
<div v-if="isLoggedIn" class="message success">
✅ 欢迎回来!你已经登录了。
</div>

<!-- v-else:条件为 false 时显示 -->
<div v-else class="message error">
❌ 你还没有登录,请先登录。
</div>
</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
isLoggedIn: false // 初始状态:未登录
}
}
}).mount('#app');
</script>
</body>
</html>

预期效果

  • 初始显示"你还没有登录"
  • 点击按钮后,切换显示"欢迎回来"
  • 按钮文字也会跟着变化

关键点

  • v-ifv-else 必须紧挨着(中间不能有其他元素)
  • v-else 不需要写条件,它会自动匹配 v-if 的相反情况

v-else-if:多条件判断

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>成绩等级判断</title>
<style>
.container {
max-width: 600px;
margin: 50px auto;
padding: 20px;
}
input {
padding: 10px;
font-size: 16px;
margin: 10px 0;
}
.grade {
padding: 20px;
border-radius: 5px;
font-size: 24px;
font-weight: bold;
text-align: center;
margin-top: 20px;
}
.excellent { background: #28a745; color: white; }
.good { background: #17a2b8; color: white; }
.pass { background: #ffc107; color: white; }
.fail { background: #dc3545; color: white; }
</style>
</head>
<body>
<div id="app">
<div class="container">
<h2>成绩等级评定</h2>

<label>请输入分数(0-100):</label>
<input type="number" v-model.number="score" min="0" max="100">

<!-- 多条件判断 -->
<div v-if="score >= 90" class="grade excellent">
🎉 优秀!继续保持!
</div>
<div v-else-if="score >= 80" class="grade good">
😊 良好!再接再厉!
</div>
<div v-else-if="score >= 60" class="grade pass">
👍 及格了!继续努力!
</div>
<div v-else-if="score >= 0" class="grade fail">
😢 不及格,加油!
</div>
<div v-else class="grade" style="background: #ccc;">
请输入有效分数
</div>
</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
score: null
}
}
}).mount('#app');
</script>
</body>
</html>

预期效果

  • 输入 95 → 显示"优秀"
  • 输入 85 → 显示"良好"
  • 输入 65 → 显示"及格"
  • 输入 50 → 显示"不及格"

二、v-show:另一种条件渲染

v-if 和 v-show 的区别

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>v-if vs v-show</title>
<style>
.box {
padding: 20px;
margin: 10px;
background: lightblue;
border-radius: 5px;
}
</style>
</head>
<body>
<div id="app">
<button @click="show = !show">切换显示</button>

<p>当前状态:{{ show ? '显示' : '隐藏' }}</p>

<!-- v-if:条件为 false 时,元素完全不存在 -->
<div v-if="show" class="box">
我使用 v-if(条件为 false 时,我会从 DOM 中移除)
</div>

<!-- v-show:条件为 false 时,元素存在但隐藏(display: none) -->
<div v-show="show" class="box">
我使用 v-show(条件为 false 时,我还在 DOM 中,只是隐藏了)
</div>

<p style="color: #999;">
💡 提示:打开浏览器的开发者工具,查看元素的变化
</p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
show: true
}
}
}).mount('#app');
</script>
</body>
</html>

对比总结

特性v-ifv-show
原理条件为 false 时,元素不会被渲染到 DOM条件为 false 时,元素仍在 DOM,只是 display: none
切换开销高(每次都要创建/销毁元素)低(只是修改 CSS)
初始开销低(条件为 false 时不渲染)高(始终会渲染)
使用场景不频繁切换的情况频繁切换的情况
配套指令可以配合 v-else 使用不能配合 v-else

记忆技巧

  • 频繁切换 → 用 v-show(就像开关灯,灯泡一直在,只是开关控制)
  • 不常切换 → 用 v-if(就像拆装家具,需要就装上,不需要就拆掉)

三、列表渲染:v-for

基本用法:循环数组

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>v-for 基础示例</title>
<style>
.container {
max-width: 600px;
margin: 50px auto;
padding: 20px;
}
.fruit-list {
list-style: none;
padding: 0;
}
.fruit-item {
padding: 15px;
margin: 10px 0;
background: #f0f0f0;
border-radius: 5px;
border-left: 4px solid #42b983;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<h2>我喜欢的水果</h2>

<!-- v-for 循环数组 -->
<ul class="fruit-list">
<li
v-for="fruit in fruits"
:key="fruit"
class="fruit-item">
🍎 {{ fruit }}
</li>
</ul>
</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
fruits: ['苹果', '香蕉', '橙子', '葡萄', '西瓜']
}
}
}).mount('#app');
</script>
</body>
</html>

预期效果

  • 显示 5 个水果名称
  • 每个水果一行

语法解释

<li v-for="fruit in fruits" :key="fruit">
  • fruit:循环时的单个元素(可以自己命名)
  • fruits:要循环的数组名
  • :key:给每个元素一个唯一标识(后面详解)

获取索引(下标)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>v-for 获取索引</title>
</head>
<body>
<div id="app">
<div class="container">
<h2>排行榜</h2>

<!-- (item, index) 可以同时获取元素和索引 -->
<ul>
<li v-for="(player, index) in players" :key="index">
第 {{ index + 1 }} 名:{{ player }}
</li>
</ul>
</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
players: ['小明', '小红', '小刚', '小美']
}
}
}).mount('#app');
</script>
</body>
</html>

预期效果

第 1 名:小明
第 2 名:小红
第 3 名:小刚
第 4 名:小美

注意index 是从 0 开始的,所以显示时要 +1


循环对象数组(常用)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>循环对象数组</title>
<style>
.student-card {
border: 2px solid #42b983;
border-radius: 10px;
padding: 20px;
margin: 15px 0;
background: #f9f9f9;
}
.student-card h3 {
margin-top: 0;
color: #42b983;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<h2>👨‍🎓 学生名单</h2>

<!-- 循环对象数组 -->
<div
v-for="student in students"
:key="student.id"
class="student-card">
<h3>{{ student.name }}</h3>
<p>年龄:{{ student.age }} 岁</p>
<p>成绩:{{ student.score }} 分</p>
</div>
</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
students: [
{ id: 1, name: '小明', age: 18, score: 95 },
{ id: 2, name: '小红', age: 17, score: 88 },
{ id: 3, name: '小刚', age: 19, score: 92 }
]
}
}
}).mount('#app');
</script>
</body>
</html>

预期效果

  • 显示 3 个学生卡片
  • 每个卡片包含姓名、年龄、成绩

四、key 属性的作用(重要)

为什么需要 key?

大白话:key 就像给每个元素一个身份证号,让 Vue 能准确识别它们。

不加 key 的问题

<!-- 没有 key -->
<li v-for="item in list">{{ item }}</li>
  • Vue 不知道哪个元素是哪个
  • 数据更新时可能出错(比如删除错了元素)

加了 key 的好处

<!-- 有 key -->
<li v-for="item in list" :key="item.id">{{ item }}</li>
  • Vue 可以准确追踪每个元素
  • 更新效率更高、更准确

key 的使用规则

正确的 key

<!-- 用唯一的 id -->
<li v-for="item in items" :key="item.id">

<!-- 用索引(仅在简单场景) -->
<li v-for="(item, index) in items" :key="index">

错误的 key

<!-- 不要用随机数 -->
<li v-for="item in items" :key="Math.random()">

<!-- 不要用重复的值 -->
<li v-for="item in items" :key="item.name"> <!-- 如果名字重复就不行 -->

最佳实践

  • 如果数据有唯一 ID,就用 ID 作为 key
  • 如果只是简单显示、不涉及增删改,可以用索引

五、条件渲染 + 列表渲染:结合使用

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>任务管理</title>
<style>
.container {
max-width: 700px;
margin: 50px auto;
padding: 20px;
}
.filter-buttons {
margin: 20px 0;
}
.filter-buttons button {
padding: 10px 20px;
margin-right: 10px;
border: none;
border-radius: 5px;
cursor: pointer;
background: #f0f0f0;
}
.filter-buttons button.active {
background: #42b983;
color: white;
}
.task-list {
list-style: none;
padding: 0;
}
.task-item {
padding: 15px;
margin: 10px 0;
background: #f9f9f9;
border-radius: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.task-item.completed {
text-decoration: line-through;
opacity: 0.6;
}
.empty {
text-align: center;
color: #999;
padding: 50px;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<h2>📋 任务管理器</h2>

<!-- 筛选按钮 -->
<div class="filter-buttons">
<button
@click="filter = 'all'"
:class="{ active: filter === 'all' }">
全部 ({{ tasks.length }})
</button>
<button
@click="filter = 'active'"
:class="{ active: filter === 'active' }">
未完成 ({{ activeTasks.length }})
</button>
<button
@click="filter = 'completed'"
:class="{ active: filter === 'completed' }">
已完成 ({{ completedTasks.length }})
</button>
</div>

<!-- 任务列表 -->
<ul class="task-list" v-if="filteredTasks.length > 0">
<li
v-for="task in filteredTasks"
:key="task.id"
:class="{ 'task-item': true, 'completed': task.completed }">
<span>{{ task.text }}</span>
<button @click="toggleTask(task.id)">
{{ task.completed ? '恢复' : '完成' }}
</button>
</li>
</ul>

<!-- 空状态 -->
<div v-else class="empty">
<p>{{ emptyMessage }}</p>
</div>
</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
filter: 'all', // 当前筛选条件
tasks: [
{ id: 1, text: '学习 Vue 基础', completed: true },
{ id: 2, text: '完成练习题', completed: false },
{ id: 3, text: '做一个小项目', completed: false },
{ id: 4, text: '复习知识点', completed: true }
]
}
},
computed: {
// 未完成的任务
activeTasks() {
return this.tasks.filter(task => !task.completed);
},
// 已完成的任务
completedTasks() {
return this.tasks.filter(task => task.completed);
},
// 根据筛选条件显示的任务
filteredTasks() {
if (this.filter === 'active') {
return this.activeTasks;
} else if (this.filter === 'completed') {
return this.completedTasks;
}
return this.tasks;
},
// 空状态提示
emptyMessage() {
if (this.filter === 'active') {
return '🎉 太棒了!没有未完成的任务!';
} else if (this.filter === 'completed') {
return '还没有完成任何任务';
}
return '还没有任务';
}
},
methods: {
// 切换任务状态
toggleTask(id) {
const task = this.tasks.find(t => t.id === id);
if (task) {
task.completed = !task.completed;
}
}
}
}).mount('#app');
</script>
</body>
</html>

预期效果

  • 点击"全部":显示所有任务
  • 点击"未完成":只显示未完成的任务
  • 点击"已完成":只显示已完成的任务
  • 点击"完成"按钮:任务被划掉
  • 如果某个类别没有任务,显示提示信息

注意:这里用到了 computed 计算属性,我们会在后面的课程详细讲解。


六、本节重点回顾

v-if / v-else-if / v-else:条件渲染,元素根据条件显示/隐藏
v-show:通过 CSS 控制显示/隐藏,适合频繁切换
v-for:循环渲染列表,语法 item in items
:key:给循环的元素添加唯一标识,提高性能和准确性
✅ 可以结合使用 v-if 和 v-for,实现复杂的列表筛选


七、练习题

练习 1:商品列表

创建一个商品列表,要求:

  1. 显示商品名称和价格
  2. 如果价格 > 100,显示"💰 高价商品"标签
  3. 如果价格 < 50,显示"🔥 特价"标签

数据参考:

products: [
{ id: 1, name: '手机', price: 3999 },
{ id: 2, name: '耳机', price: 299 },
{ id: 3, name: '数据线', price: 29 },
{ id: 4, name: '充电器', price: 99 }
]
点击查看答案
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>商品列表</title>
<style>
.product-item {
padding: 15px;
margin: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
.tag {
display: inline-block;
padding: 3px 10px;
border-radius: 3px;
font-size: 12px;
margin-left: 10px;
}
.expensive {
background: #ff6b6b;
color: white;
}
.cheap {
background: #51cf66;
color: white;
}
</style>
</head>
<body>
<div id="app">
<h2>商品列表</h2>
<div v-for="product in products" :key="product.id" class="product-item">
<strong>{{ product.name }}</strong>
<span>¥{{ product.price }}</span>
<span v-if="product.price > 100" class="tag expensive">💰 高价商品</span>
<span v-if="product.price < 50" class="tag cheap">🔥 特价</span>
</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
products: [
{ id: 1, name: '手机', price: 3999 },
{ id: 2, name: '耳机', price: 299 },
{ id: 3, name: '数据线', price: 29 },
{ id: 4, name: '充电器', price: 99 }
]
}
}
}).mount('#app');
</script>
</body>
</html>

练习 2:动态添加删除列表项

在第 4 节的任务清单基础上,添加:

  1. 每个任务前面显示序号
  2. 如果任务列表为空,显示提示"还没有任务,快添加一个吧!"
点击查看答案
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>改进的任务清单</title>
</head>
<body>
<div id="app">
<h2>任务清单</h2>
<input type="text" v-model="newTask" @keyup.enter="addTask">
<button @click="addTask">添加</button>

<!-- 任务列表 -->
<ul v-if="tasks.length > 0">
<li v-for="(task, index) in tasks" :key="index">
{{ index + 1 }}. {{ task }}
<button @click="deleteTask(index)">删除</button>
</li>
</ul>

<!-- 空状态 -->
<p v-else style="color: #999;">还没有任务,快添加一个吧!</p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
newTask: '',
tasks: []
}
},
methods: {
addTask() {
if (this.newTask.trim()) {
this.tasks.push(this.newTask);
this.newTask = '';
}
},
deleteTask(index) {
this.tasks.splice(index, 1);
}
}
}).mount('#app');
</script>
</body>
</html>

🎉 恭喜你完成第五节课!

下一节我们将学习:组件基础 —— 学会创建和使用 Vue 组件,让代码更模块化、更易维护。