用 v-for 把一個數(shù)組 對應為 一組元素
我們使用 v-for 指令根據(jù)數(shù)組的列表來進行渲染。
v-for指令需要使用 item,idx in items這樣特殊的語法, items是源數(shù)據(jù)數(shù)組,item則是數(shù)組中的每一項。idx代表索引,但是idx不是必須的。
<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' }
]
}
})
列表輸入 foo bar兩項
在 v-for 塊中,我們擁有對父級作用域的完全訪問權,v-for還支持一個可選的第二參數(shù)作為當前項的索引。
<ul id="example-2">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
var example2 = new Vue({
el: '#example-2',
data: {
parentMessage: 'Parent',
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})
也可以使用 of 代替 in作為分隔符,如下:
<div v-for="item of items"></div>
一個對象的 v-for
我們也可以用 v-for 通過一個對象的屬性來迭代,而不只是數(shù)組。
<ul id="v-for-object" class="demo">
<li v-for="value in object">
{{ value }}
</li>
</ul>
new Vue({
el: '#v-for-object',
data: {
object: {
firstName: 'John',
lastName: 'Doe',
age: 30
}
}
})
輸出 john doe 30
我們也可以提供使用第二個參數(shù)作為鍵名
<div v-for="(value, key) in object">
{{ key }}: {{ value }}
</div>
輸出
firstName:john
lastName:doe
age:30
也就是說,在使用數(shù)組循環(huán)的時候,參數(shù)分別為:item當前項和第二參數(shù)索引index
在使用對象循環(huán)時,參數(shù)分別為 當前想item的value,item的key和他的索引index
key
當 vuejs 使用 v-for 更新已經(jīng)渲染過的元素列表時,它默認用“就地復用”策略。
如果數(shù)據(jù)項的順序改變,vue也不會移動dom來匹配數(shù)據(jù)項的順序,而只是簡單的復用每個元素,并且確保它在特定索引下顯示已被渲染過的每個元素。
這個默認的模式是高效的,但是只適用于不依賴子組件狀態(tài)或臨時 DOM 狀態(tài) (例如:表單輸入值) 的列表渲染輸出。
為了給 Vue 一個提示,以便它能跟蹤每個節(jié)點的身份,從而重用和重新排序現(xiàn)有元素,你需要為每項提供一個唯一 key 屬性。理想的 key 值是每項都有的且唯一的 id。這個特殊的屬性相當于 Vue 1.x 的 track-by ,但它的工作方式類似于一個屬性,所以你需要用 v-bind 來綁定動態(tài)值 (在這里使用簡寫):
<div v-for="item in items" :key="item.id">
<!-- 內(nèi)容 -->
</div>
建議盡可能在使用 v-for 時提供 key,除非遍歷輸出的 DOM 內(nèi)容非常簡單,或者是刻意依賴默認行為以獲取性能上的提升。
因為它是 Vue 識別節(jié)點的一個通用機制,key 并不與 v-for 特別關聯(lián),key 還具有其他用途,我們將在后面的指南中看到其他用途。
數(shù)組更新檢測
變異方法
vue中還包含一些數(shù)組變異的方法,所以他們也會觸發(fā)視圖的更新。
Vue 包含一組觀察數(shù)組的變異方法,所以它們也將會觸發(fā)視圖更新。這些方法如下:
push() //數(shù)組最后插入添加一個
pop() //刪除數(shù)組的最后一個元素
shift() //刪除數(shù)組的第一個元素
unshift() //數(shù)組開頭添加一個或多個元素
splice() //法向/從數(shù)組中添加/刪除項目
sort() //數(shù)組排序
reverse() //顛倒數(shù)組中元素的排序
//demo
example1.items.push({ message: 'Baz' }) 。
替換數(shù)組
以上的變異方法,顧名思義會改變原始的數(shù)組,相比之下,也有非變異的方法。例如:filter(), concat() 和 slice() 。這些不會改變原始數(shù)組,但總是返回一個新數(shù)組。當使用非變異方法時,可以用新數(shù)組替換舊數(shù)組:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
你可能認為這將導致 Vue 丟棄現(xiàn)有 DOM 并重新渲染整個列表。幸運的是,事實并非如此。Vue 為了使得 DOM 元素得到最大范圍的重用而實現(xiàn)了一些智能的、啟發(fā)式的方法,所以用一個含有相同元素的數(shù)組去替換原來的數(shù)組是非常高效的操作。
注意事項
由于 js 的限制,vue不能夠檢測到以下數(shù)組的變化
1.當使用索引直接對一個項進行設置時
vm.items[indexOfItem] = newValue
2.當修改數(shù)組長度時
vm.items.length = newLength
舉例:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是響應性的
vm.items.length = 2 // 不是響應性的
以上雖然可以修改,但是vue檢測不到,所以就不是響應式的。
這里列舉了 解決第一類問題的2種方法:
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
//分別是 數(shù)組、數(shù)組索引、新添加對應索引的值
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue);
//分別是 數(shù)組的索引位置,添加個數(shù),添加的值
解決第二類問題的方法
vm.items.splice(newLength)
對象更改檢測注意事項
由于js 的限制,vue不能檢測對象屬性的添加和刪除
var vm = new Vue({
data: {
a: 1
}
})
// `vm.a` 現(xiàn)在是響應式的
vm.b = 2
// `vm.b` 不是響應式的,因為這里的b是新添加的,無法檢測到。
對于已經(jīng)創(chuàng)建的實例,vue不能夠動態(tài)添加根級別的響應式屬性,但是,可以使用 Vue.set(object, key, value) 方法向嵌套對象添加響應式屬性。例如,對于:
var vm = new Vue({
data: {
userProfile: {
name: 'Anika'
}
}
})
這時可以添加一個新的 age 屬性到嵌套的 userProfile 對象:
Vue.set(vm.userProfile, 'age', 27)
有時候可能需要對已有對象賦多個新屬性,比如使用 Object.assign() 或 _.extend()。在這種情況下,你應該用兩個對象的屬性創(chuàng)建一個新的對象。
vm.userProfile = Object.assign({}, vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})
顯示過濾排序結果
有時候需要顯示一個數(shù)組的過濾或者排序的副本,而不實際改變或者重置原始數(shù)據(jù),這種情況下,可以創(chuàng)建返回過濾或者排序數(shù)組的計算屬性。
<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
})
}
}
//這里返回能夠被2整除的 2和4
如果在計算屬性不適合的情況下則使用 methods
一段范圍內(nèi)的取值
v-for也可以取整數(shù)。在這種情況下,它將重復多次模板。
//循環(huán)10次
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
v-for on a <template>
類似 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 with v-if
當他們處于同一個節(jié)點時,v-for的優(yōu)先級高于v-if,也就是說,先進行循環(huán)再判斷。
比如下面這個列表,我們只想渲染部分節(jié)點,這時候v-if判斷就很有用。
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>
上面的代碼只傳遞了未完成的 todos。
如果我們的目的是先進行判斷,在看是否有必要循環(huán),就可以將 v-if包裹在外層的div中。
<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也是必須填寫的。
然而任何數(shù)組都不會被自動傳遞到組件中,因為組件有自己獨立的作用域,為了把迭代的數(shù)據(jù)傳遞到組件里,我們要使用props。
<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 的運作緊密耦合。明確組件數(shù)據(jù)的來源能夠使組件在其他場合重復使用。
下面是一個簡單的 todo list 的完整例子:
<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> 元素內(nèi)只有 <li> 元素會被看作有效內(nèi)容。這樣做實現(xiàn)的效果與 <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 = ''
}
}
})