列表循环 v-for
将一个数组对应为一组元素
v-for
指令根据数组选项列表进行渲染,使用时需使用item in items
形式的特殊语法。items
是源数据数组且item
是数组元素迭代的别名。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
<div id="app" class="container">
<template v-if="true">
<h1 class="page-header">title</h1>
<ul class="list-group">
<li class="list-group-item" v-for="item in items">{{item.username}}</li>
</ul>
</template>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var vm = new Vue({
el:'#app',
data:{
items:[
{id:1, username:'alice'},
{id:2, username:'ben'},
{id:3, username:'carl'},
]
}
});
</script>
</body>
</html>
v-for
块中,拥有对父作用域属性的完全访问权限。v-for
支持一个可选第二个参数为当前项的索引。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
<div id="app" class="container">
<template v-if="true">
<h1 class="page-header">title</h1>
<ul class="list-group">
<li class="list-group-item" v-for="(item,index) in items">
{{title}}{{index+1}}
<span class="pull-right">{{item.username}}</span>
</li>
</ul>
</template>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var vm = new Vue({
el:'#app',
data:{
title:'user',
items:[
{id:1, username:'alice'},
{id:2, username:'ben'},
{id:3, username:'carl'},
]
}
});
</script>
</body>
</html>
可使用of
替换in
作为分隔符,因为它最接近JS迭代器的语法
<div v-for="item of items"></div>
一个对象的 v-for
可使用v-for
通过一个对象的属性来迭代
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
<div id="app" class="container">
<template v-if="true">
<h1 class="page-header">title</h1>
<ul class="list-group">
<li class="list-group-item" v-for="val in user">
{{val}}
</li>
</ul>
</template>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var vm = new Vue({
el:'#app',
data:{
user:{
name:'junchow',
gender:'male',
age:31
}
}
});
</script>
</body>
</html>
v-for
提供第二个参数为键名
<li class="list-group-item" v-for="(val,key) in user">
<strong>{{key}}</strong>
<span class="pull-right">{{val}}</span>
</li>
v-for
提供第三个参数为索引
<li class="list-group-item" v-for="(val,key,idx) in user">
<strong>{{idx}} {{key}}</strong>
<span class="pull-right">{{val}}</span>
</li>
在遍历对象时,是按Object.keys()
的结果遍历,不能保证其结果在不同的JS引擎下是一致的。
key
当Vue.js用v-for
正在更新已渲染过的元素列表时,默认使用"就地复用"策略。若数据项的顺序被改变,Vue将不会移动DOM元素来匹配数据项的顺序,而是简单复用此处每个元素,且确保它在特定索引下显示已被渲染过的每个元素。这个默认的模式是高效的,但只适用于不依赖子组件状态或临时DOM状态的列表渲染输出。
为了给Vue一个提示,以便能跟踪每个节点的身份,从而重用和重排现有元素,需为每项提供一个唯一的key
属性。理想的key
值是每项都有且仅有唯一的id
。这个特殊的属性的工作方式类似于一个属性,需使用v-bind
来绑定动态值。
<li class="list-group-item" v-for="item in items" :key="item.id">
</li>
建议尽可能在使用v-for
时提供key
,除非遍历输出的DOM内容非常简单,或是刻意依赖默认行为以获取性能上的提升。
因为它是Vue识别节点的一个通用机制,key
并不与v-for
特别关联,key
具有其他用途。
数组更新检测
变异方法
Vue包含一组观察数组的变异方法,它们会触发视图更新。
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
app.items = app.items.push({message:'foo'});
替换数组
- 变异方法(mutation method)顾名思义,会改变方法调用的原始数组。
- 非变异方法(non-mutating method)不会改变原始数组,总是返回一个新数组。
当使用非变异方法时,可用新数组替换旧数组。
- filter()
- concat()
- slice()
app.items = app.items.filter(function(item){
return item.message.match(/foo/);
});
非变异方法并不会导致Vue丢弃现有DOM并重新渲染整个列表,Vue为了使得DOM元素得到最大范围的重用而实现了一些智能的、启发式的方法,所以用一个含有相同元素的数组去替代原来的数组是非常高效的。
注意事项
由于JS自身限制Vue不能检测以下变动的数组:
- 当利用索引直接设置一个项时
vm.items[index]=newVal
- 当修改数组的长度时
vm.items.length=newLen
为解决第一类问题,以下两种方式都可以实现和vm.items[index]=newVal
相同的效果,同样也触发状态更新。
// Vue.set
Vue.set(app.items, index, newVal)
// Array.prototype.splice
app.items.splice(index, 1, newVal);
为了解决第二类问题可使用splice
app.items.splice(newLen)
对象更改检测注意事项
由于JS限制Vue无法检测对象属性的添加或删除
var vm = new Vue({
data:{
message:'hello'
}
});
//vm.message是响应式的
vm.username = 'alice';//非响应式
对于已经创建的实例,Vue无法动态添加根级别的响应式属性。但可使用Vue.set(obj,key,val)
方式向嵌套对象添加响应式属性。
var vm = new Vue({
data:{
profile:{
name:'alice'
}
}
});
//添加新属性到嵌套的profile对象
Vue.set(vm.profile, 'age', 20);
//使用vm.$set实例方法,它只是全局Vue.set的别名。
vm.$set(this.profile, 'age', 20);
有时可能需为已有对象赋予多个新属性,如使用Object.assign()
或_.extend()
。在这种情况下,用两个对象的属性创建一个新对象。若想添加新的响应式属性:
this.profile = Object.assign({}, this.profile, {age:31, color:'green'})
显示过滤/排序结果
想要显示一个数组的过滤或排序副本,而非实际改变或重置原始数据。此情况下可创建返回过滤或排序数组的计算属性。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
<div id="app" class="container">
<template v-if="true">
<h1 class="page-header">title</h1>
<ul class="list-group">
<li class="list-group-item" v-for="num in even">
{{num}}
</li>
</ul>
</template>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var vm = new Vue({
el:'#app',
data:{
numbers:[11,2,43,14,5,26,17,8,29]
},
computed:{
even:function(){
return this.numbers.filter(function(number){
return number%2===0;
})
}
}
});
</script>
</body>
</html>
当计算属性不适用情况下,如在嵌套v-for
循环中,可使用method
方法。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
<div id="app" class="container">
<template v-if="true">
<h1 class="page-header">title</h1>
<ul class="list-group">
<li class="list-group-item" v-for="item in even(numbers)">
{{item}}
</li>
</ul>
</template>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var vm = new Vue({
el:'#app',
data:{
numbers:[11,2,43,14,5,26,17,8,29]
},
methods:{
even:function(items){
return items.filter(function(item){
return item%2===0;
})
}
}
});
</script>
</body>
</html>
取值范围的v-for
v-for
可取整,此情况下将重复多次模板。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
<div id="app" class="container">
<template v-if="true">
<h1 class="page-header">title</h1>
<ul class="list-group">
<li class="list-group-item" v-for="item in 10">
{{item}}
</li>
</ul>
</template>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var vm = new Vue({
el:'#app',
data:{
}
});
</script>
</body>
</html>
v-for
on a <template>
类似于v-if
也可利用带有v-for
的<template>
渲染多个元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
<div id="app" class="container">
<template v-if="true">
<h1 class="page-header">title</h1>
<ul class="list-group">
<template v-for="item in items">
<li class="list-group-item">{{item.username}}</li>
<li class="divider"></li>
</template>
</ul>
</template>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var vm = new Vue({
el:'#app',
data:{
items:[
{username:'alice'},
{username:'ben'},
{username:'carl'},
{username:'deny'},
]
}
});
</script>
</body>
</html>
v-for
with v-if
当它们处于同一节点,v-for
的优先级比v-if
更高,这意味着v-if
将分别重复运行于每个v-for
循环中。当你想为仅有的一些项渲染节点时,这种优先级的机制十分有用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
<div id="app" class="container">
<template v-if="true">
<h1 class="page-header">title</h1>
<ul class="list-group">
<li class="list-group-item" v-for="item in items" v-if="item.show">{{item.username}}</li>
</ul>
</template>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var vm = new Vue({
el:'#app',
data:{
items:[
{username:'alice', show:true},
{username:'ben', show:true},
{username:'carl', show:false},
{username:'deny', show:true},
]
}
});
</script>
</body>
</html>
若需有条件地跳过循环的执行,可将v-if
置于外层元素或<template>
上。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
<div id="app" class="container">
<template v-if="true">
<h1 class="page-header">title</h1>
<ul class="list-group" v-if="items.length">
<li class="list-group-item" v-for="item in items" v-if="item.show">{{item.username}}</li>
</ul>
<p class="alert alert-warning" v-else>no data...</p>
</template>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
var vm = new Vue({
el:'#app',
data:{
items:[
{username:'alice', show:true},
{username:'ben', show:true},
{username:'carl', show:false},
{username:'deny', show:true},
]
}
});
</script>
</body>
</html>
一个组件的 v-for
在自定义组件中,可像普通元素一样使用v-for
。
<item class="list-group-item" v-for="item in items" :key="item.id" v-if="item.show">{{item.username}}</item>
在Vue2.2.0+版本中,当在组件中使用v-for
时,key
是必须的。然后在任何数据都不会被自动传递到数组里,以为组件有自己独立的作用域。为了把迭代数据传递到数组里需使用props
。
<item class="list-group-item"
v-for="(item,index) in items"
v-bind:item="item"
v-bind:index="index"
v-bind:key="item.id"
></item>
不自动将item
注入到组件里的原因是,这会使得组件与v-for
的运作紧密耦合。明确组件数据的来源能够使组件在其他场合重复使用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
<div id="app" class="container">
<template v-if="true">
<h1 class="page-header">TODO-LIST</h1>
<input v-model="title" v-on:keyup.enter="create" class="form-control" placeholder="add a todo" />
<hr>
<ul class="list-group" v-if="items.length">
<li class="list-group-item"
is="item"
v-for="(item,index) in items"
v-bind:title="item.title"
v-bind:index="index"
v-bind:key="item.id"
v-on:remove="items.splice(index,1)"
></li>
</ul>
<p class="alert alert-warning" v-else>no data...</p>
</template>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
Vue.component('item',{
template:"<li>{{title}} <a class='pull-right' v-on:click='$emit(\"remove\")'>×</a></li>",
props:['title']
})
var vm = new Vue({
el:'#app',
data:{
title:'',
items:[
{id:1,title:'java'},
{id:2,title:'javascript'},
{id:3,title:'python'},
{id:4,title:'vue'},
],
next:5
},
methods:{
create:function(){
this.items.push({
id:this.next++,
title:this.title
});
this.title = '';
}
}
});
</script>
</body>
</html>