本文主要包含以下知识点:
- 使用 v-for 进行列表渲染
- 数组更新检测
使用 v-for 进行列表渲染
使用 v-for 指令根据一组数组的选项列表进行渲染。v-for 指令需要使用 item in items 形式的特殊语法,items 是源数据数组,item 是数组元素迭代的别名。
下面是一个 v-for 的示例:
<body>
<div id="app">
<p v-for="item in items">my name is {{ item.name }},I'm {{ item.age }} years old.</p>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
items: [
{'name' : 'xiejie','age' : 18},
{'name' : 'yajing','age' : 20},
{'name' : 'xizhi','age' : 2}
]
}
});
</script>
</body>
效果:
v-for 还支持一个可选的第二个参数为当前项的索引。
示例如下:
<body>
<div id="app">
<p v-for="(item,index) in items">{{ index+1 }}. my name is {{ item.name }},I'm {{ item.age }} years old.</p>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
items: [
{'name' : 'xiejie','age' : 18},
{'name' : 'yajing','age' : 20},
{'name' : 'xizhi','age' : 2}
]
}
});
</script>
</body>
效果:
也可以使用 v-for 来迭代一个对象。
示例如下:
<body>
<div id="app">
<ul>
<!-- 第 1 个参数为值 , 第 2 个参数为键 , 第 3 个参数为索引 -->
<li v-for="(value,key,index) in stu">{{ index+1 }}. {{ key }} : {{ value }}</li>
</ul>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
stu: {
name: 'xiejie',
age: 18,
gender: 'male',
score: 100
}
}
});
</script>
</body>
效果:
v-for 也可以取整数。在这种情况下,它将重复多次模板。
例如:
<body>
<div id="app">
<ul>
<li v-for="n in 5">{{ n }}</li>
</ul>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script>
const vm = new Vue({
el: '#app',
});
</script>
</body>
效果:
类似于 v-if,可以利用带有 v-for 的 template 渲染多个元素。
比如:
<body>
<div id="app">
<ul>
<template v-for="item in items">
<li>name:{{ item.name }}</li>
<li>age:{{ item.age }}</li>
<li>gender:{{ item.male }}</li>
<hr>
</template>
</ul>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script>
const vm = new Vue({
el: '#app',
data : {
items : [
{'name':'xiejie','age':18,'gender':'male'},
{'name':'yajing','age':20,'gender':'female'},
{'name':'xizhi','age':2,'gender':'male'},
]
}
});
</script>
</body>
效果:
使用 key
当 Vue 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。
如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态(例如:表单输入值)的列表渲染输出。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,往往需要为每项提供一个唯一的 key 属性。
理想的 key 值是每项都有的唯一 id。
例如:
<div v-for="item in items" :key="item.id">
<!-- 内容 -->
</div>
数组更新检测
当数组里面的数据发生更新时,Vue 会检测到其更新,然后自动重新渲染视图。更新数组的方法可以分为 2 大类:变异方法和非变异方法。
变异方法
所谓变异方法,是指在调用了该方法后会改变原本的数组。
Vue 包含一组观察数组的变异方法,它们将会触发视图更新。
具体包含以下方法:
- push:接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。
- pop:从数组末尾移除最后一项,减少数组的 length 值,然后返回移除的项。
- shift:移除数组中的第一个项并返回该项,同时数组的长度减 1。
- unshift:在数组前端添加任意个项并返回新数组长度。
- splice:删除原数组的一部分成员,并可以在被删除的位置添加入新的数组成员。
- sort:调用每个数组项的 toString 方法,然后比较得到的字符串排序,返回经过排序之后的数组。
- reverse:用于反转数组的顺序,返回经过排序之后的数组。
下面我们来看一个具体的示例:
<body>
<div id="example">
<div>
<button @click='push'>push</button>
<button @click='pop'>pop</button>
<button @click='shift'>shift</button>
<button @click='unshift'>unshift</button>
<button @click='splice'>splice</button>
<button @click='sort'>sort</button>
<button @click='reverse'>reverse</button>
</div>
<ul>
<li v-for="item in items">
{{ item.name }}
</li>
</ul>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script>
const vm = new Vue({
el: '#example',
data: {
items: [
{ name : 'xiejie'},
{ name : 'yajing'},
{ name : 'xizhi'}
],
addValue: { name: 'newGuy'}
},
methods: {
push() {
this.items.push(this.addValue)
},
pop() {
this.items.pop()
},
shift() {
this.items.shift()
},
unshift() {
this.items.unshift(this.addValue)
},
splice() {
this.items.splice(0, 1)
},
sort() {
this.items.sort()
},
reverse() {
this.items.reverse()
}
}
})
</script>
</body>
效果:
非变异方法
除了上面所列举的变异方法以外,JavaScript 中也存在不会改变原来数组的非变异方法。这个时候,我们可以使用新数组来替换旧数组。
常见的非变异方法如下:
- concat:先创建当前数组的副本,然后将接收到的参数添加到该副本的末尾,最后返回新构建的数组。
- slice:基于当前数组中一个或多个项创建一个新数组,接受一个或两个参数,即要返回项的起始和结束位置,最后返回新数组。
- map:对数组的每一项运行给定函数,返回每次函数调用的结果组成的数组。
- filter:对数组中的每一项运行给定函数,该函数会返回 true 的项组成的数组。
具体示例如下:
<body>
<div id="app">
<div>
<button @click='concat'>concat</button>
<button @click='slice'>slice</button>
<button @click='map'>map</button>
<button @click='filter'>filter</button>
</div>
<ul>
<li v-for="item in items">
{{ item }}
</li>
</ul>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
items: ['xiejie', 'yajing', 'xizhi'],
addValue: 'newGuy'
},
methods: {
concat() {
this.items = this.items.concat(this.addValue); // 拼接新的值
},
slice() {
this.items = this.items.slice(1); // 移除第一个数据
},
map() {
// 每一项数据前面拼接索引
this.items = this.items.map(function (item, index, arr) {
return index + item;
})
},
filter() {
// 返回索引大于 0 的数据
this.items = this.items.filter(function (item, index, arr) {
return (index > 0);
});
}
}
})
</script>
</body>
效果:
无法检测的数组变化
在 Vue 中无法检测到以下 2 种变异的形式:
- 利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
- 修改数组的长度时,例如:vm.items.length = newLength
不过,针对上面的 2 种情况,我们也有相应的对策。
如果要设置数组某一个具体的项目,可以使用 Vue 实例的 set 方法。而如果要改变数组的长度,可以使用 splice 来进行替代。
下面是一个具体的示例:
<body>
<div id="app">
<!-- 特别需要注意的是以下的数组变动方法是不支持的:
通过索引直接设置项:app.books[2] = {...}
修改数组长度:app.books.length = 1 -->
<input type="text" name="" id="" placeholder="请输入书名" v-model='newBookName'>
<input type="text" name="" id="" placeholder="请输入作者" v-model='newBookAuthor'>
<button @click='editOne'>设置第一本书的信息</button>
<p>购买的书如下:</p>
<ul>
<template v-for='(book,index) in books'>
<li>序号:{{index}}</li>
<li>书名:{{book.name}}</li>
<li>作者:{{book.author}}</li>
<li>-------------------------</li>
</template>
</ul>
<button @click='remainOne'>只留下第一本书</button>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
newBookName: '', // 和第一个文本框的数据相关联
newBookAuthor: '', // 和第二个文本框的数据相关联
books: [
{'name': '科学用脑','author': '郭勇'},
{'name': 'Vue入门','author': '梁灏'},
{'name': '日语会话','author': '谢杰'},
{'name': 'PS高级技巧','author': '沈文靖'},
]
},
methods: {
// 如果要设置数组某一个具体的项目,可以使用 Vue 实例的 set 方法
editOne: function () {
Vue.set(this.books, 0, {
'name': this.newBookName,
'author': this.newBookAuthor
});
},
// 如果要改变数组的长度,可以使用splice来进行替代
remainOne: function () {
this.books.splice(1);
}
}
});
</script>
</body>
效果:设置第一本书信息的功能涉及到了改变数组具体的某一个元素,需要使用 Vue 实例的 set 方法。而只留下第一本书的功能,涉及到了改变数组的长度,在不能直接设置数组 length 属性的情况下,我们选择使用 splice 方法来实现。
注:还可以使用 vm.$set 实例方法,它只是全局 Vue.set 的别名。
显示过滤和排序结果
有时,我们想要显示一个数组的过滤或排序副本,而不实际改变或重置原始数据。
在这种情况下,可以创建返回过滤或排序数组的计算属性。
例如:
<body>
<div id="app">
<ul>
<li v-for="n in evenNumbers">{{ n }}</li>
</ul>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
numbers: [1, 2, 3, 4, 5]
},
computed: {
evenNumbers: function () {
return this.numbers.filter(function (number) {
return number % 2 === 0
})
}
}
});
// 原有的数组没有改变
console.log(vm.numbers); // [1, 2, 3, 4, 5]
</script>
</body>
效果:
注:在计算属性不适用的情况下,可以使用一个 method 方法来代替计算属性。
总结
-
使用 v-for 可以进行列表渲染。v-for 指令需要使用 item in items 形式的特殊语法。
-
当 Vue 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。
-
Vue 包含一组观察数组的变异方法,它们将会触发视图更新。
-
如果是非变异方法,我们可以使用新数组来替换旧数组。
-
如果要设置数组某一个具体的项目,可以使用 Vue 实例的 set 方法。而如果要改变数组的长度,可以使用 splice 来进行替代。
-
如果想要显示一个数组的过滤或排序副本,而不实际改变或重置原始数据,可以创建返回过滤或排序数组的计算属性。