[toc]
你可以使用 v-repeat 指令來基于 ViewModel 上的對象數組渲染列表。對于數組中的每個對象,該指令將創建一個以該對象作為其 $data 對象的子 Vue 實例。這些子實例繼承父實例的數據作用域,因此在重復的模板元素中你既可以訪問子實例的屬性,也可以訪問父實例的屬性。此外, 你還可以通過 $index 屬性來獲取當前實例對應的數組索引。
**示例:**
~~~
<ul id="demo">
<li v-repeat="items" class="item-{{$index}}">
{{$index}} - {{parentMsg}} {{childMsg}}
</li>
</ul>
var demo = new Vue({
el: '#demo',
data: {
parentMsg: 'Hello',
items: [
{ childMsg: 'Foo' },
{ childMsg: 'Bar' }
]
}
})
~~~
**結果:**
~~~
0 - Hello Foo
1 - Hello Bar
~~~
## 塊級重復
有時我們可能需要重復一個包含多個節點的塊,這時可以用 <template> 標簽包裹這個塊。這里的 <template> 標簽只起到語義上的包裹作用,其本身不會被渲染出來。例如:
~~~
<ul>
<template v-repeat="list">
<li>{{msg}}</li>
<li class="divider"></li>
</template>
</ul>
~~~
## 簡單值數組
簡單值 (primitive value) 即字符串、數字、boolean 等并非對象的值。對于包含簡單值的數組,你可用 $value 直接訪問值:
~~~
<ul id="tags">
<li v-repeat="tags">
{{$value}}
</li>
</ul>
new Vue({
el: '#tags',
data: {
tags: ['JavaScript', 'MVVM', 'Vue.js']
}
})
~~~
**結果:**
~~~
JavaScript
MVVM
Vue.js
~~~
## 使用別名
有時我們可能想要更明確地訪問當前作用域的變量而不是隱式地回退到父作用域。你可以通過提供一個參數給 v-repeat 指令并用它作為將被迭代項的別名:
~~~
<ul id="users">
<li v-repeat="user in users">
{{user.name}} - {{user.email}}
</li>
</ul>
new Vue({
el: '#users',
data: {
users: [
{ name: 'Foo Bar', email: 'foo@bar.com' },
{ name: 'John Doh', email: 'john@doh.com' }
]
}
})
~~~
**結果:**
~~~
Foo Bar - foo@bar.com
John Doh - john@doh.com
~~~
這里的 user in users 語法只在 Vue 0.12.8 及其以上版本中可用。老版本需要使用 user : users 語法。
在 v-repeat 中使用別名會讓模板可讀性更強,同時性能更好。
## 變異方法
Vue.js 內部對被觀察數組的變異方法 (mutating methods,包括 push(), pop(), shift(), unshift(), splice(), sort() 和 reverse()) 進行了攔截,因此調用這些方法也將自動觸發視圖更新。
~~~
// 以下操作會觸發 DOM 更新
demo.items.unshift({ childMsg: 'Baz' })
demo.items.pop()
~~~
## 擴展方法
Vue.js 給被觀察數組添加了兩個便捷方法:$set() 和 $remove() 。
你應該避免直接通過索引來設置數據綁定數組中的元素,比如 demo.items[0] = {},因為這些改動是無法被 Vue.js 偵測到的。你應該使用擴展的 $set() 方法:
~~~
// 不要用 `demo.items[0] = ...`
demo.items.$set(0, { childMsg: 'Changed!'})
~~~
$remove() 只是 splice()方法的語法糖。它將移除給定索引處的元素。當參數不是數值時,$remove() 將在數組中搜索該值并刪除第一個發現的對應元素。
~~~
// 刪除索引為 0 的元素。
demo.items.$remove(0)
~~~
## 替換數組
當你使用非變異方法,比如filter(), concat() 或 slice(),返回的數組將是一個不同的實例。在此情況下,你可以用新數組替換舊的數組:
~~~
demo.items = demo.items.filter(function (item) {
return item.childMsg.match(/Hello/)
})
~~~
你可能會認為這將導致整個列表的 DOM 被銷毀并重新渲染。但別擔心,Vue.js 能夠識別一個數組元素是否已有關聯的 Vue 實例, 并盡可能地進行有效復用。
## 使用 track-by
在某些情況下,你可能需要以全新的對象(比如 API 調用所返回的對象)去替換數組。如果你的每一個數據對象都有一個唯一的 id 屬性,那么你可以使用 track-by 特性參數給 Vue.js 一個提示,這樣它就可以復用已有的具有相同 id 的 Vue 實例和 DOM 節點。
例如,如果你的數據長這樣:
~~~
{
items: [
{ _uid: '88f869d', ... },
{ _uid: '7496c10', ... }
]
}
那么你可以像這樣給出提示:
<div v-repeat="items" track-by="_uid">
`<!-- content -->
</div>
~~~
在替換 items 數組時,Vue.js 如果碰到一個有 _uid: '88f869d' 的新對象,它就會知道可以直接復用有同樣 _uid 的已有實例。
如果沒有唯一的 id 可以用來追蹤,也可以使用 track-by="$index",也就是原地復用實例和 DOM 節點。請務必小心使用此功能,因為在追蹤 $index 時,Vue 不會移動子實例及 DOM 節點,只是按他們第一次被創建時的順序復用。有兩種情形應避免使用 track-by="$index":
1. 當重復的塊包含表單輸入框,且輸入框綁定的值可能導致數組被重新排序;
2. 在重復組件時,組件除了接收的數組數據外,還包含私有的狀態。
> 在使用全新數據重新渲染大型 v-repeat 列表時,合理使用 track-by 能極大地提升性能。
## 遍歷對象
你也可以使用 v-repeat 遍歷一個對象的所有屬性。每個重復的實例會有一個特殊的屬性 $key。對于簡單值,你也可以象訪問數組中的簡單值那樣使用 $value 屬性。
~~~
<ul id="repeat-object">
<li v-repeat="primitiveValues">{{$key}} : {{$value}}</li>
<li>===</li>
<li v-repeat="objectValues">{{$key}} : {{msg}}</li>
</ul>
new Vue({
el: '#repeat-object',
data: {
primitiveValues: {
FirstName: 'John',
LastName: 'Doe',
Age: 30
},
objectValues: {
one: {
msg: 'Hello'
},
two: {
msg: 'Bye'
}
}
}
})
~~~
結果:
~~~
FirstName : John
LastName : Doe
Age : 30
===
one : Hello
two : Bye
~~~
在 ECMAScript 5 中,對于給對象添加一個新屬性,或是從對象中刪除一個屬性時,沒有機制可以檢測到這兩種情況。針對這個問題,Vue.js 中的被觀察對象會被添加三個擴展方法: $add(key, value), $set(key, value) 和 $delete(key)。這些方法可以被用于在添加 / 刪除觀察對象的屬性時觸發對應的視圖更新。方法 $add 和 $set 的不同之處在于當指定的鍵已經在對象中存在時 $add 將提前返回,所以調用 obj.$add(key) 并不會以 undefined 覆蓋已有的值。
迭代值域
v-repeat 也可以接受一個整數。在這種情況下,它將重復顯示一個模板多次。
~~~
<div id="range">
<div v-repeat="val">Hi! {{$index}}</div>
</div>```
```new Vue({
el: '#range',
data: {
val: 3
}
});
~~~
結果:
~~~
Hi! 0
Hi! 1
Hi! 2
~~~
數組過濾器
有時候我們只需要顯示一個數組的過濾或排序過的版本,而不需要實際改變或重置原始數據。Vue 提供了兩個內置的過濾器來簡化此類需求: filterBy 和 orderBy。可查看 方法文檔 獲得更多細節。