1. 数组的循环
用 v-for
指令根据一组数组的选项列表进行渲染。
1.1 通过索引渲染数组内容
通过数组的索引获取数组的数据
html
<div id="app">
<ul>
<li>{{ fruites[0] }}</li>
<li>{{ fruites[1] }}</li>
<li>{{ fruites[2] }}</li>
<li>{{ fruites[3] }}</li>
</ul>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
fruites:["苹果","梨子","西瓜","榴莲"]
},
})
</script>
这种写法在数据很多的时候或者数据发生更新的时候处理就会很繁琐, 因此我们可以使用v-for指令来循环数组
1.2 数组循环语法
1.2.1语法说明:
v-for
指令需要使用item in items
形式的特殊语法,item
是数组元素迭代的别名。items
是原数据数组
1.2.2v-for
指令的语法使用示例:
vue
<ul id="example-1">
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
1.2.3 数组循环示例
基本数组的循环
html
<div id="app">
<ul>
<li v-for="fruite in fruites">
{{fruite}}
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
fruites:["苹果","梨子","西瓜","榴莲"]
},
})
</script>
1.2.4 获取数组索引
v-for 还支持一个可选的第二个参数为当前项的索引。
html
<div id="app">
<ul>
<li v-for="(fruite,index) in fruites">
{{fruite}}--{{index}}
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
fruites:["苹果","梨子","西瓜","榴莲"]
},
})
</script>
1.2.5 数组项为对象
数组项为对象的循环
html
<div id="app">
<ul>
<li v-for="fruite in fruites">
<span>{{fruite.name}}:</span>
<span>{{fruite.price}}</span>
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
fruites:[
{
name:"苹果",
price: "5元/斤"
},
{
name:"梨子",
price: "6元/斤"
},
{
name:"西瓜",
price: "8元/斤"
},
{
name:"榴莲",
price: "12元/斤"
}
]
}
})
</script>
使用索引
html
<div id="app">
<ul>
<li v-for="(fruite,index) in fruites">
<span>{{index+1}}</span>
<span>{{fruite.name}}:</span>
<span>{{fruite.price}}</span>
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
fruites:[
{
name:"苹果",
price: "5元/斤"
},
{
name:"梨子",
price: "6元/斤"
},
{
name:"西瓜",
price: "8元/斤"
},
{
name:"榴莲",
price: "12元/斤"
}
]
}
})
</script>
1.2.6 使用of
循环
同时我们也可以用of
替代in
作为分割符
语法
html
<div v-for="item of items"></div>
示例:
html
<div id="app">
<ul>
<li v-for="(fruite,index) of fruites">
{{fruite}}--{{index}}
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
fruites:["苹果","梨子","西瓜","榴莲"]
},
})
</script>
2. 对象的循环
也可以用 v-for
指令来循环对象。
第一个参数是训练遍历对象的属性值:
html
<div id="app">
<ul>
<li v-for="value in users">
{{ value }}
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
users:{
name:'Henry',
age: 22,
work: "前端工程师",
like: "看书"
}
}
})
</script>
第二个的参数为对象的属性(键名):
html
<div id="app">
<ul>
<li v-for="(value,key) in users">
{{key}} : {{ value }}
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
users:{
name:'Henry',
age: 22,
work: "前端工程师",
like: "看书"
}
}
})
</script>
还可以通过第三个参数来获取索引值:
html
<div id="app">
<ul>
<li v-for="(value,key,index) in users">
{{index}}. {{key}} : {{ value }}
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
users:{
name:'Henry',
age: 22,
work: "前端工程师",
like: "看书"
}
}
})
</script>
3. v-for循环中的 key属性
使用v-for
更新已渲染的元素列表时,默认用就地复用
策略;列表数据修改的时候,他会根据key值去判断某个值是否修改,如果修改,则重新渲染这一项,否则复用之前的元素; 我们在使用的使用经常会使用index
(即数组的下标)来作为key
,但其实这是不推荐的一种使用方法;
key值的使用其实是和vue响应式已经虚拟DOM有关, 那么我们通过下面的例子来了解一下
例子:
数据
js
new Vue({
el: "#app",
data: {
fruites:[
{
id: 1,
name: '苹果',
},
{
id: 2,
name: '梨子',
},
{
id: 3,
name: '西瓜',
},
]
}
})
页面渲染
html
<div v-for="(item, index) in list" :key="index" >{{item.name}}</div>
但是数据发生了变化,
js
new Vue({
el: "#app",
data: {
fruites:[
{
id: 1,
name: '苹果',
},
{
id: 2,
name: '梨子',
},
{
id: 3,
name: '西瓜',
},
{
id: 4,
name: '这是新增的水果',
},
]
}
})
如果数据是这一种变化的话, 那么index没什么问题
数据前后变化的结果
之前的数据 之后的数据
key:0 index: 0 name: "苹果" key:0 index: 0 name: "苹果"
key:1 index: 1 name: "梨子" key:1 index: 1 name: "梨子"
key:2 index: 3 name: "西瓜" key:2 index: 2 name: "西瓜"
key:3 index: 4 name: "这是新增的水果"
这样vue就会分析到 其他的数据 都不需要改变,只需要在新增一个DOM节点,然后添加新增的数据就可以了
可以输入我们是在数组中间插入的数据就会不一样为了
js
new Vue({
el: "#app",
data: {
fruites:[
{
id: 1,
name: '苹果',
},
{
id: 4,
name: '这是新增的水果',
},
{
id: 2,
name: '梨子',
},
{
id: 3,
name: '西瓜',
},
]
}
})
这时数据的对比
之前的数据 之后的数据
key:0 index: 0 name: "苹果" key:0 index: 0 name: "苹果"
key:1 index: 1 name: "梨子" key:1 index: 1 name: "这是新增的水果"
key:2 index: 3 name: "西瓜" key:2 index: 2 name: "梨子"
key:3 index: 3 name: "西瓜"
通过上面清晰的对比,发现除了第一个数据可以复用之前的之外,另外三条数据都需要重新渲染;
是不是很惊奇,我明明只是插入了一条数据,怎么三条数据都要重新渲染?而我想要的只是新增的那一条数据新渲染出来就行了
最好的办法是使用数组中不会变化的那一项作为key
值,对应到项目中,即每条数据都有一个唯一的id
,来标识这条数据的唯一性;使用id
作为key
值,我们再来对比一下向中间插入一条数据,此时会怎么去渲染
html
<div v-for="(item, index) in list" :key="index" >{{item.name}}</div>
此时数据的变化
之前的数据 之后的数据
key:0 id: 0 index: 0 name: "苹果" key:0 id: 0 index: 0 name: "苹果"
key:1 id: 1 index: 1 name: "梨子" key:4 id: 4 index: 1 name: "这是新增的水果"
key:2 id: 2 index: 3 name: "西瓜" key:1 id: 1 index: 2 name: "梨子"
key:2 id: 2 index: 3 name: "西瓜"
现在对比发现只有一条数据变化了,就是id
为4的那条数据,因此只要新渲染这一条数据就可以了,其他都是就复用之前的;
为什么需要key属性: 虚拟DOM的diff算法,
当某一层有很多相同的节点时,也就是列表节点时,Diff算法的更新过程
我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的:
即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?
所以我们需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。
所以一句话,key的作用主要是为了高效的更新虚拟DOM。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。
建议尽可能在使用 v-for
时提供 key
,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
4 不推荐同时使用 v-if
和 v-for
。
v-if和v-for一起使用,v-for的优先级要高于v-if
可能会想到v-if和v-for是用的两种情况
-
为了过滤一个列表中的项目
-
为了避免渲染本应该被隐藏的列表
那么接下来好好看看这两种情况
第一种情况: 为了过滤一个列表中的项目
为了过滤项目内容,我们可能会如下调用:
html
<li v-for = "fruite of fruites" v-if = "fruite.price > 22"></li>
在这种情况下,请将users
替换为一个计算属性(比如activeUsers
),让其返回过滤后的列表。
示例详解:
html
<div id="app">
<ul>
<li
v-for="fruite of fruites"
:key="fruite.id"
>
名称: {{ fruite.name}} -- 价格: {{ fruite.price}}元/斤
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
fruites:[
{
id: 1,
name: '苹果',
price: 20,
},
{
id: 4,
name: '桃子',
price: 30,
},
{
id: 2,
name: '梨子',
price: 10,
},
{
id: 3,
name: '西瓜',
price: 50,
},
]
}
})
</script>
如果现在只想显示价格在22元以上的水果,我们可能会这么写
html
<ul>
<li
v-for="fruite of fruites"
v-if="fruite.price > 22"
:key="fruite.id"
>
名称: {{ fruite.name}} -- 价格: {{ fruite.price}}元/斤
</li>
</ul>
这么写固然会得到你想要的效果, 但是因为v-for和v-if优先级的关系, 所以将会经过如下的运算
js
this.fruites.map(function (fruite) {
if (fruite.price > 22) {
return user.name
}
})
因此,哪怕我们只渲染出一小部分内容,也得在每次重新渲染的时候遍历整个列表,无论价格是否满足我们的条件
随意我们推荐使用计算属性, 在计算属性中处理过滤事宜, 计算属性会在计算完毕后缓存内容,提高遍历的效率
html
<div id="app">
<ul>
<li
v-for="fruite of filterFruites"
:key="fruite.id"
>
名称: {{ fruite.name}} -- 价格: {{ fruite.price}}元/斤
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
fruites:[
{
id: 1,
name: '苹果',
price: 20,
},
{
id: 4,
name: '桃子',
price: 30,
},
{
id: 2,
name: '梨子',
price: 10,
},
{
id: 3,
name: '西瓜',
price: 50,
},
]
},
computed: {
filterFruites(){
return this.fruites.filter(fruite => fruite.price > 22)
}
}
})
</script>
这样我们得到的结果是一样的,但是我们获得了如下的好处
- 过滤后的列表只会在 fruites 数组发生相关变化时才被重新运算,过滤更高效。
- 使用
v-for = "fruite of filterFruites"
之后,我们在渲染过滤后的数据,渲染更高效。 - 解藕渲染层的逻辑,可维护性 (对逻辑的更改和扩展) 更强。
第二种情况: 为了避免渲染本应该被隐藏的列表
也就是根据条件类判断列表的显示我们也后可能会使用下面的方法调用
html
<li v-for = "fruite of fruites" v-if = "isShow"></li>
实例详解:
html
<div id="app">
<ul>
<li
v-for="fruite of fruites"
v-if="isShow"
:key="fruite.id"
>
名称: {{ fruite.name}} -- 价格: {{ fruite.price}}元/斤
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: "#app",
data: {
isShow: true,
fruites:[
{
id: 1,
name: '苹果',
price: 20,
},
{
id: 4,
name: '桃子',
price: 30,
},
{
id: 2,
name: '梨子',
price: 10,
},
{
id: 3,
name: '西瓜',
price: 50,
},
]
}
})
</script>
原理是一样的, 就是如果这么写, 还是会循环遍历每一个数据,然后判断是不是显示. 一样浪费
所以我们将 v-if 移动到容器元素,这样我们就不用对每一个元素都进行判断是否显示, 取而代之的是,我们只检查判断一次,且不会在 isShow
为假的时候还循环运算 v-for。
html
<div id="app">
<ul v-if="isShow">
<li
v-for="fruite of fruites"
:key="fruite.id"
>
名称: {{ fruite.name}} -- 价格: {{ fruite.price}}元/斤
</li>
</ul>
</div>
5.显示(过滤/排序)结果
有时,我们想要显示一个数组的过滤或排序副本,而不实际改变或重置原始数据。在这种情况下,可以使用计算属性和方法来过滤数据
我们上面讲过了计算属性,下面来看看方法的使用
html
<!-- HTML -->
<li v-for="n in even(numbers)">{{ n }}</li>
<!-- js -->
<script>
new Vue({
el: "#app",
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
even: function (numbers) {
return numbers.filter(function (number) {
return number % 2 === 0
})
}
}
})
</script>
6 二维数组循环
html
<div id="app">
<ul>
<li v-for='(fruit,index) in fruites'>
{{index+1}}. {{fruit.name}}
<ul>
<li v-for="(c,childrenIndex) in fruit.color">
{{index+1}}.{{childrenIndex+1}} {{c}}
</li>
</ul>
</li>
</ul>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
fruites: [
{ name: '香蕉', color: ['green', 'yellow'] },
{ name: '苹果', color: ['red', 'yellow', 'green'] },
{ name: '西瓜', color: ['pink'] },
]
}
})
</script>
7 其他不常用的循环
html
<!-- 循环字符串 -->
<div id="app">
<div v-for="c in 'abcd'">{{c}}</div>
</div>
<!-- 循环数字 -->
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
<!-- 借用template来循环嵌套多个元素 -->
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
总结示例:
html
<!-- vue-app 是根容器 -->
<div id='vue-app'>
<h1> v-for 循环</h1>
<!-- 数组下标获取 -->
{{ characters[0] }}
{{ characters[1] }}
{{ characters[2] }}
<!-- 遍历数组 -->
<ul>
<li v-for="character in characters"> {{ character }}</li>
</ul>
<ul>
<li v-for = "user in users">
{{ user.name }} - {{ user.age }}
</li>
</ul>
<!-- 获取数组的下标(数组的每一项,数组的下标) -->
<ul>
<li v-for = "(user,index) in users">
{{ index }}. {{ user.name }} - {{ user.age }}
</li>
</ul>
<!-- 不适用列表标签的循环 -->
<div v-for = "(user,index) in users">
<h2>{{ index }}. {{ user.name }}</h2>
<p>{{ user.age }}</p>
</div>
<!-- 上边的div渲染处理后时空标签,<template>去除空标签 -->
<template v-for = "(user,index) in users">
<h2>{{ index }}. {{ user.name }}</h2>
<p>{{ user.age }}</p>
</template>
<!-- <template>标签是不会进行渲染的 -->
<!-- 循环json数据的key值和val值 -->
<template v-for = "(user,index) in users">
<div v-for="(val,key) in user">
{{ index }}. {{key}}:{{val}}
</div>
</template>
</div>
<script>
//实例化Vue对象
new Vue({
el: "#vue-app",
data: {
characters:["苹果","梨子","西瓜"],
users:[
{name:"苹果",price: 30},
{name:"梨子",price: 25},
{name:"西瓜",price: 18}
]
}
})
</script>