v-for指令基于一个数组来渲染一个列表。它是 Vue 中最常用的指令之一,用于展示动态数据。
<ul id="app">
<li v-for="item in items">
{{ item.text }}
</li>
</ul>
<script>
new Vue({
el: '#app',
data: {
items: [
{ text: '学习 JavaScript' },
{ text: '学习 Vue' },
{ text: '做个项目' }
]
}
})
</script>
<ul>
<li v-for="(item, index) in items">
{{ index }} - {{ item.text }}
</li>
</ul>
<ul id="app">
<li v-for="value in object">
{{ value }}
</li>
</ul>
<script>
new Vue({
el: '#app',
data: {
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
})
</script>
<div v-for="(value, key) in object">
{{ key }}: {{ value }}
</div>
<div v-for="(value, key, index) in object">
{{ index }}. {{ key }}: {{ value }}
</div>
v-for 也可以遍历整数:
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
输出:1 2 3 4 5 6 7 8 9 10
和 v-if 类似,可以使用 <template> 渲染多个元素:
<ul>
<template v-for="item in items">
<li>{{ item.name }}</li>
<li class="divider"></li>
</template>
</ul>
Vue 对以下数组方法进行了包装,调用它们会触发视图更新:
| 方法 | 说明 |
|---|---|
push() | 末尾添加 |
pop() | 末尾删除 |
shift() | 开头删除 |
unshift() | 开头添加 |
splice() | 删除/插入 |
sort() | 排序 |
reverse() | 反转 |
// 这些方法会触发更新
this.items.push({ text: '新项目' })
this.items.pop()
this.items.splice(0, 1)
非变异方法(如 filter、concat、slice)不会改变原数组,而是返回新数组:
// 需要替换原数组
this.items = this.items.filter(function(item) {
return item.isActive
})
Vue 无法检测以下变化
// ❌ 通过索引直接设置项
this.items[index] = newValue
// ✅ 使用 Vue.set 或 $set
Vue.set(this.items, index, newValue)
this.$set(this.items, index, newValue)
// ❌ 修改数组长度
this.items.length = newLength
// ✅ 使用 splice
this.items.splice(newLength)
对于对象,Vue 也有限制:
// ❌ 添加新属性,Vue 无法检测
this.user.newProp = 'value'
// ✅ 使用 Vue.set
Vue.set(this.user, 'newProp', 'value')
// ✅ 使用 Object.assign 创建新对象
this.user = Object.assign({}, this.user, { newProp: 'value' })
使用计算属性:
<li v-for="n in evenNumbers">{{ n }}</li>
<script>
new Vue({
data: {
numbers: [1, 2, 3, 4, 5]
},
computed: {
evenNumbers: function() {
return this.numbers.filter(function(number) {
return number % 2 === 0
})
}
}
})
</script>
或者使用方法(需要传参时):
<ul>
<li v-for="n in even(numbers)">{{ n }}</li>
</ul>
<script>
new Vue({
data: {
numbers: [1, 2, 3, 4, 5]
},
methods: {
even: function(numbers) {
return numbers.filter(function(number) {
return number % 2 === 0
})
}
}
})
</script>
<table>
<thead>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(user, index) in users" :key="user.id">
<td>{{ user.name }}</td>
<td>{{ user.age }}</td>
<td>
<button @click="edit(index)">编辑</button>
<button @click="remove(index)">删除</button>
</td>
</tr>
</tbody>
</table>
<div v-for="category in categories" :key="category.id">
<h3>{{ category.name }}</h3>
<ul>
<li v-for="item in category.items" :key="item.id">
{{ item.name }}
</li>
</ul>
</div>
<div v-for="todo in todos" :key="todo.id">
<input type="checkbox" v-model="todo.done">
<span :class="{ done: todo.done }">{{ todo.text }}</span>
</div>
<style>
.done {
text-decoration: line-through;
color: #999;
}
</style>
<div v-for="item in paginatedItems" :key="item.id">
{{ item.name }}
</div>
<button @click="prevPage" :disabled="currentPage === 1">上一页</button>
<span>第 {{ currentPage }} 页</span>
<button @click="nextPage" :disabled="currentPage === totalPages">下一页</button>
<script>
new Vue({
computed: {
paginatedItems: function() {
var start = (this.currentPage - 1) * this.pageSize
var end = start + this.pageSize
return this.items.slice(start, end)
},
totalPages: function() {
return Math.ceil(this.items.length / this.pageSize)
}
}
})
</script>
不要在同一元素上使用 v-if 和 v-for:
<!-- ❌ 不推荐 -->
<li v-for="user in users" v-if="user.isActive">
{{ user.name }}
</li>
<!-- ✅ 推荐:使用计算属性 -->
<li v-for="user in activeUsers" :key="user.id">
{{ user.name }}
</li>
<script>
computed: {
activeUsers: function() {
return this.users.filter(function(user) {
return user.isActive
})
}
}
</script>
如果必须使用,将 v-for 放在外层:
<template v-for="user in users" :key="user.id">
<li v-if="user.isActive">
{{ user.name }}
</li>
</template>
| 用法 | 说明 |
|---|---|
v-for="item in items" | 遍历数组 |
v-for="(item, index) in items" | 带索引 |
v-for="(value, key) in object" | 遍历对象 |
v-for="n in 10" | 遍历整数 |
记住:
v-for 添加 :key$set 更新数组v-if 和 v-for