用 v-for 把一個數組對應爲一組元素
我們可以用 v-for 指令基於一個數組來渲染一個列表。 v-for 指令需要使用 item in items 形式的特殊語法, 其中 items 是源數據數組,而 items 是被迭代的數組元素的別名。
在 v-for 塊中,我們可以訪問所有父作用域的屬性。 v-for 還支持第二個參數,即當前選項的索引。
當然,我們也可以用 of 替代 in 作爲分隔符,因爲它更接近於 JavaScript 迭代器的語法:
在 v-for 裏使用對象
你也可以用 v-for 來遍歷一個對象的屬性:
當然也可以提供第二個的參數爲 property 名稱,也就是鍵名:
還可以用第三個參數作爲索引:
注意:在遍歷對象時,會按 Object.keys() 的結果遍歷,但是不能保證它的結果在不同的 JavaScript 引擎下都一致。
維護狀態
當vue 正在更新使用 v-for 渲染的元素列表時,它默認使用的就是“就地更新”的策略。如果數據項的順序被改變, Vue 將不會移動 DOM 元素來匹配數據項的順序,而是就地更新每個元素,並且確保它們在每個索引位置正確索引。這個類似 Vue 1.x 的 track-by=$index 。
這個默認的模式是高效的,但是隻適用於不依賴子組件狀態或臨時 DOM 狀態(例如:表單輸入值)的列表渲染輸出。
爲了給 Vue 一個提示,以便它能跟蹤每個節點的身份,從而重用和重新排序現有元素,你需要爲每項提供一個唯一的 key 屬性:
<div v-for="item in items" v-bind:key="item.id">
<!-- 內容 -->
</div>
建議儘可能在使用 v-for 時提供 可以 attribute,除非遍歷輸出的 DOM 內容非常簡單,或者是刻意依賴默認行爲以獲取性能上的提升。
因爲它是 Vue 識別節點的一個通用機制, Key 並不僅與 v-for 特別關聯。後面我們將在指南中看到,它還具有其他用途。
不要使用對象或數組之類的非基本類型值作爲 v-for 的 key 。請用字符串或數值類型的值。
數組更新檢測
變異方法:(mutation method)
Vue 將被偵聽的數組的變異方法進行了包裹,所以它們也將會觸發師徒更新。這些被包裹的方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
你可以打開控制檯,然後對前面例子的 items 數組嘗試調用變異方法。比如:
example1.items.push({message:'baz'});
替換數組
變異方法,顧名思義,會改變調用了這些方法的原始數組。相比之下,也有非變異( non-mutating method )方法,例如 filter() 、concat()、和 slice()。它們不會改變原始數組,而總是返回一個新數組。當使用非變異方法時,可以使用新數組替換舊數組:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
你可能認爲這將導致 Vue 丟棄現有 DOM 並重新渲染整個列表。幸運的是,事實並非如此。 Vue 爲了使得 DOM 得到最大範圍的重用而實現了一些智能的啓發式方法,所以用一個含有相同元素的數組去替換原來的數組是非常有效的操作。
#注意事項:
由於 JavaScript 的限制, Vue不能檢測以下數組的變動:
1.當你利用索引直接設置一個數組項時,例如: vm.items[indexOfItem] = newValue
2.當你修改數組的長度時,例如: vm.items.length = newLength
舉個例子:
var vm = new Vue({
data: {
items:['q','w','e']
}
})
vm.items[1] = 'a' //這不是響應式的
vm.items.length = 2 //這也不是響應式的
爲了解決第一類問題,以下兩種方式都可以實現和 vm.items[indexOfItem] = newValue 相同的效果,同時也將在響應式系統內觸發狀態更新:
//Vue.set
Vue.set(vm.items, indexOfItem, newValue)
//Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
你也可以使用 vm.$set 實例方法,該方法是全局方法 Vue.set 的一個別名:
vm.$set(vm.items, indexOfItem, newValue)
爲了解決第二類問題,你可以使用 splice:
vm.items.splice(newLength)
對象變更檢測注意事項
還是由於 JavaScript 的限制,Vue不能檢測對象的添加或刪除:
var vm = new Vue({
data: {
a: 1
}
})
// vm.a 現在是響應式的
vm.b = 2
// vm.b 不是響應式的
對於已經創建的實例,Vue 不允許動態添加根級別的響應式屬性。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套對象添加響應式屬性。 例如,對於:
var vm = new Vue({
data: {
userProfile: {
name: 'zzk'
}
}
})
//可以添加一個新的 age 屬性到嵌套的 userProfile對象:
Vue.set(vm.userProfile, 'age', 27)
//還可以使用 vm.$set 實例方法,它只是全局 Vue.set 的別名:
vm.$set(vm.userProfile, 'age', 27)
有時可能需要爲已有對象賦值多個屬性,比如使用 Object.assign() 或 _.extend()。在這種情況下,你應該用兩個對象的屬性創建一個新的對象。所以,如果你想添加新的響應式屬性,不要像這樣:
Object.assign(vm.userProfile,{
age:27,
favoriteColor:'China Red'
})
上面是錯誤的示範,正確的如下:
vm.userPorfile = Object.assign({}, vm.userProfile, {
age:27,
favoriteColor:'China Red'
})
顯示過濾/排序後的結果
有時,我們想要顯示一個數組經過過濾或排序後的版本,而不實際改變或重置原始數據。在這種情況下,可以創建一個計算屬性,來返回過濾或排序後的數組。
例如:
<li v-for="n in evenNumbers">{{ n }}</li>
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
evenNumbers: function () {
return this.numbers.filter(function (number) {
return number % 2 === 0
})
}
}
在計算屬性不適用的情況下 (例如,在嵌套 v-for
循環中) 你可以使用一個方法:
<li v-for="n in even(numbers)">{{ n }}</li>
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
even: function (numbers) {
return numbers.filter(function (number) {
return number % 2 === 0
})
}
}
在 V-for 裏使用值範圍
v-for 也可以接受整數,在這種情況下,它會把模板重複對應次數。
<div>
<span v-for="n in 10">{{ n }}</span>
</div>
結果:
1 2 3 4 5 6 7 8
在 <template> 上使用 v-for
類似於 v-if, 你也可以利用帶有 v-for 的 <template> 來循環渲染一段包含多個元素的內容。比如:
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
v-for 與 v-if 一同使用
注意我們不推薦在同一元素上使用 v-if 和 v-for。
當它們處於同一節點,v-for
的優先級比 v-if
更高,這意味着 v-if
將分別重複運行於每個 v-for
循環中。當你只想爲部分項渲染節點時,這種優先級的機制會十分有用,如下:
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>
上面的代碼將只渲染未完成的 todo。
而如果你的目的是有條件地跳過循環的執行,那麼可以將 v-if
置於外層元素 (或 <template>
)上。如:
<ul v-if="todos.length">
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>
<p v-else>No todos left!</p>
在組件上使用 v-for
這部分內容假定你已經瞭解組件相關知識。你也完全可以先跳過它,以後再回來查看。
在自定義組件上,你可以像在任何普通元素上一樣使用 v-for
。
<my-component v-for="item in items" :key="item.id"></my-component>
2.2.0+ 的版本里,當在組件上使用
v-for
時,key
現在是必須的。
然而,任何數據都不會被自動傳遞到組件裏,因爲組件有自己獨立的作用域。爲了把迭代數據傳遞到組件裏,我們要使用 prop:
<my-component
v-for="(item, index) in items"
v-bind:item="item"
v-bind:index="index"
v-bind:key="item.id"
></my-component>
不自動將 item
注入到組件裏的原因是,這會使得組件與 v-for
的運作緊密耦合。明確組件數據的來源能夠使組件在其他場合重複使用。
下面是一個簡單的 todo 列表的完整例子:
<div id="todo-list-example">
<form v-on:submit.prevent="addNewTodo">
<label for="new-todo">Add a todo</label>
<input
v-model="newTodoText"
id="new-todo"
placeholder="E.g. Feed the cat"
>
<button>Add</button>
</form>
<ul>
<li
is="todo-item"
v-for="(todo, index) in todos"
v-bind:key="todo.id"
v-bind:title="todo.title"
v-on:remove="todos.splice(index, 1)"
></li>
</ul>
</div>
注意這裏的 is="todo-item"
屬性。這種做法在使用 DOM 模板時是十分必要的,因爲在 <ul>
元素內只有 <li>
元素會被看作有效內容。這樣做實現的效果與 <todo-item>
相同,但是可以避開一些潛在的瀏覽器解析錯誤。查看 DOM 模板解析說明 來了解更多信息。
Vue.component('todo-item', {
template: '\
<li>\
{{ title }}\
<button v-on:click="$emit(\'remove\')">Remove</button>\
</li>\
',
props: ['title']
})
new Vue({
el: '#todo-list-example',
data: {
newTodoText: '',
todos: [
{
id: 1,
title: 'Do the dishes',
},
{
id: 2,
title: 'Take out the trash',
},
{
id: 3,
title: 'Mow the lawn'
}
],
nextTodoId: 4
},
methods: {
addNewTodo: function () {
this.todos.push({
id: this.nextTodoId++,
title: this.newTodoText
})
this.newTodoText = ''
}
}
})