列表循環 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" />
</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" />
</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" />
</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" />
</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" />
</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" />
</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" />
</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" />
</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" />
</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" />
</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>