可以使用 v-for
指令基于一個(gè)數(shù)組渲染一個(gè)列表。這個(gè)指令使用特殊的語法,形式為 item in items
,items
是數(shù)據(jù)數(shù)組,item
是當(dāng)前數(shù)組元素的別名:
示例:
<ul id="example-1">
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
var example1 = new Vue({
el: '#example-1',
data: {
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})
結(jié)果:
{% raw %}
<ul id="example-1" class="demo">
<li v-for="item in items">
{{item.message}}
</li>
</ul>
<script>
var example1 = new Vue({
el: '#example-1',
data: {
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
},
watch: {
items: function () {
smoothScroll.animateScroll(null, '#example-1')
}
}
})
</script>
{% endraw %}
在 v-for
塊內(nèi)我們能完全訪問父組件作用域內(nèi)的屬性,另有一個(gè)特殊變量 $index
,正如你猜到的,它是當(dāng)前數(shù)組元素的索引:
<ul id="example-2">
<li v-for="item in items">
{{ parentMessage }} - {{ $index }} - {{ item.message }}
</li>
</ul>
var example2 = new Vue({
el: '#example-2',
data: {
parentMessage: 'Parent',
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})
結(jié)果:
{% raw%}
<ul id="example-2" class="demo">
<li v-for="item in items">
{{ parentMessage }} - {{ $index }} - {{ item.message }}
</li>
</ul>
<script>
var example2 = new Vue({
el: '#example-2',
data: {
parentMessage: 'Parent',
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
},
watch: {
items: function () {
smoothScroll.animateScroll(null, '#example-2')
}
}
})
</script>
{% endraw %}
另外,你可以為索引指定一個(gè)別名(如果 v-for
用于一個(gè)對(duì)象,則可以為對(duì)象的鍵指定一個(gè)別名):
<div v-for="(index, item) in items">
{{ index }} {{ item.message }}
</div>
類似于 template v-if
,也可以將 v-for
用在 <template>
標(biāo)簽上,以渲染一個(gè)包含多個(gè)元素的塊。例如:
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider"></li>
</template>
</ul>
Vue.js 包裝了被觀察數(shù)組的變異方法,故它們能觸發(fā)視圖更新。被包裝的方法有:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
你可以打開瀏覽器的控制臺(tái),用這些方法修改上例的 items
數(shù)組。例如:example1.items.push({ message: 'Baz' })
。
變異方法,如名字所示,修改了原始數(shù)組。相比之下,也有非變異方法,如 filter()
, concat()
和 slice()
,不會(huì)修改原始數(shù)組而是返回一個(gè)新數(shù)組。在使用非變異方法時(shí),可以直接用新數(shù)組替換舊數(shù)組:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
可能你覺得這將導(dǎo)致 Vue.js 棄用已有 DOM 并重新渲染整個(gè)列表——幸運(yùn)的是并非如此。 Vue.js 實(shí)現(xiàn)了一些啟發(fā)算法,以最大化復(fù)用 DOM 元素,因而用另一個(gè)數(shù)組替換數(shù)組是一個(gè)非常高效的操作。
有時(shí)需要用全新對(duì)象(例如通過 API 調(diào)用創(chuàng)建的對(duì)象)替換數(shù)組。因?yàn)?v-for
默認(rèn)通過數(shù)據(jù)對(duì)象的特征來決定對(duì)已有作用域和 DOM 元素的復(fù)用程度,這可能導(dǎo)致重新渲染整個(gè)列表。但是,如果每個(gè)對(duì)象都有一個(gè)唯一 ID 的屬性,便可以使用 track-by
特性給 Vue.js 一個(gè)提示,Vue.js 因而能盡可能地復(fù)用已有實(shí)例。
例如,假定數(shù)據(jù)為:
{
items: [
{ _uid: '88f869d', ... },
{ _uid: '7496c10', ... }
]
}
然后可以這樣給出提示:
<div v-for="item in items" track-by="_uid">
<!-- content -->
</div>
然后在替換數(shù)組 items
時(shí),如果 Vue.js 遇到一個(gè)包含 _uid: '88f869d'
的新對(duì)象,它知道它可以復(fù)用這個(gè)已有對(duì)象的作用域與 DOM 元素。
如果沒有唯一的鍵供追蹤,可以使用 track-by="$index"
,它強(qiáng)制讓 v-for
進(jìn)入原位更新模式:片斷不會(huì)被移動(dòng),而是簡單地以對(duì)應(yīng)索引的新值刷新。這種模式也能處理數(shù)據(jù)數(shù)組中重復(fù)的值。
這讓數(shù)據(jù)替換非常高效,但是也會(huì)付出一定的代價(jià)。因?yàn)檫@時(shí) DOM 節(jié)點(diǎn)不再映射數(shù)組元素順序的改變,不能同步臨時(shí)狀態(tài)(比如 <input>
元素的值)以及組件的私有狀態(tài)。因此,如果 v-for
塊包含 <input>
元素或子組件,要小心使用 track-by="$index"
因?yàn)?JavaScript 的限制,Vue.js 不能檢測到下面數(shù)組變化:
vm.items[0] = {}
;vm.items.length = 0
。為了解決問題 (1),Vue.js 擴(kuò)展了觀察數(shù)組,為它添加了一個(gè) $set()
方法:
// 與 `example1.items[0] = ...` 相同,但是能觸發(fā)視圖更新
example1.items.$set(0, { childMsg: 'Changed!'})
至于問題 (2),只需用一個(gè)空數(shù)組替換 items
。
除了 $set()
, Vue.js 也為觀察數(shù)組添加了 $remove()
方法,用于從目標(biāo)數(shù)組中查找并刪除元素,在內(nèi)部它調(diào)用 splice()
。因此,不必這樣:
var index = this.items.indexOf(item)
if (index !== -1) {
this.items.splice(index, 1)
}
只用這樣:
this.items.$remove(item)
也可以使用 v-for
遍歷對(duì)象。除了 $index
之外,作用域內(nèi)還可以訪問另外一個(gè)特殊變量 $key
。
<ul id="repeat-object" class="demo">
<li v-for="value in object">
{{ $key }} : {{ value }}
</li>
</ul>
new Vue({
el: '#repeat-object',
data: {
object: {
FirstName: 'John',
LastName: 'Doe',
Age: 30
}
}
})
結(jié)果:
{% raw %}
<ul id="repeat-object" class="demo">
<li v-for="value in object">
{{ $key }} : {{ value }}
</li>
</ul>
<script>
new Vue({
el: '#repeat-object',
data: {
object: {
FirstName: 'John',
LastName: 'Doe',
Age: 30
}
}
})
</script>
{% endraw %}
也可以給對(duì)象的鍵提供一個(gè)別名:
<div v-for="(key, val) in object">
{{ key }} {{ val }}
</div>
在遍歷對(duì)象時(shí),是按 `Object.keys()` 的結(jié)果遍歷,但是不能保證它的結(jié)果在不同的 JavaScript 引擎下是一致的。
v-for
也可以接收一個(gè)整數(shù),此時(shí)它將重復(fù)模板數(shù)次。
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
結(jié)果:
{% raw %}
<div id="range" class="demo">
<span v-for="n in 10">{{ n }} </span>
</div>
<script>
new Vue({ el: '#range' })
</script>
{% endraw %}
有時(shí)我們想顯示過濾/排序過的數(shù)組,同時(shí)不實(shí)際修改或重置原始數(shù)據(jù)。有兩個(gè)辦法:
filterBy
和 orderBy
。計(jì)算屬性有更好的控制力,也更靈活,因?yàn)樗侨δ?JavaScript。但是通常過濾器更方便,詳細(xì)見 API。