Vue.js(五)列表渲染 v-for
v-for="item in items " // 數組更新檢測 // 對象更改檢測註意事項 // 顯示過濾 / 排序結果 // 一段取值範圍的 v-for //
v-for
- 數組:我們用 v-for 指令根據一組數組的選項列表進行渲染。v-for 指令需要使用 item in items 形式的特殊語法,items 是源數據數組並且 item 是數組元素叠代的別名。
<ul id="example-1"> <li v-for="item in items"> {{ item.message }} </li> </ul> var
在 v-for 塊中,我們擁有對父作用域屬性的完全訪問權限。v-for 還支持一個可選的第二個參數為當前項的索引
<ul id="example-2"> <li v-for="(item, index) in items
- 對象:用 v-for 通過一個對象的屬性來叠代
<ul> <li v-for="value in object"> {{ value }} </li> </ul> var app = new Vue({ el:"#app", data:{ object:{ tit:"標題", con:"內容", time:"中午" } } }) ==> ·標題 ·內容
可以提供第二個的參數為鍵名
<ul> <li v-for="(value,key) in object"> {{ key }} : {{ value }} </li> </ul> ==> ·tit : 標題 ·con : 內容 ·time : 中午
第三個參數為索引
<ul> <li v-for="(value,key) in object"> {{ index }}. : {{ key }} : {{ value }} </li> </ul> ==> ·0. tit : 標題 ·1. con : 內容 ·2. time : 中午
在遍歷對象時,是按 Object.keys() 的結果遍歷,但是不能保證它的結果在不同的 JavaScript 引擎下是一致的
- key:
當 Vue.js 用 v-for 正在更新已渲染過的元素列表時,它默認用“就地復用”策略。如果數據項的順序被改變,Vue 將不會移動 DOM 元素來匹配數據項的順序, 而是簡單復用此處每個元素,並且確保它在特定索引下顯示已被渲染過的每個元素。這個類似 Vue 1.x 的 track-by="$index" 。
這個默認的模式是高效的,但是只適用於不依賴子組件狀態或臨時 DOM 狀態 (例如:表單輸入值) 的列表渲染輸出。
為了給 Vue 一個提示,以便它能跟蹤每個節點的身份,從而重用和重新排序現有元素,你需要為每項提供一個唯一 key 屬性。理想的 key 值是每項都有的且唯一的 id。這個特殊的屬性相當於 Vue 1.x 的 track-by ,但它的工作方式類似於一個屬性,所以你需要用 v-bind 來綁定動態值 (在這裏使用簡寫):
<div v-for="item in items" :key="item.id"> <!-- 內容 --> </div>
建議盡可能在使用 v-for 時提供 key,除非遍歷輸出的 DOM 內容非常簡單,或者是刻意依賴默認行為以獲取性能上的提升。 因為它是 Vue 識別節點的一個通用機制,key 並不與 v-for 特別關聯,key 還具有其他用途,我們將在後面的指南中看到其他用途。
數組更新檢測
- 變異方法:Vue 包含一組觀察數組的變異方法,所以它們也將會觸發視圖更新。這些方法如下:
* push() * pop() * shift() * unshift() * splice() * sort() * reverse()
以上例子:app.object.push({ message: ‘Baz‘ }) 將在列表尾部增加 Baz
- 數組替換:變異方法 (mutation method),顧名思義,會改變被這些方法調用的原始數組。相比之下,也有非變異 (non-mutating method) 方法,例如:filter(), concat() 和 slice() 。這些不會改變原始數組,但總是返回一個新數組。當使用非變異方法時,可以用新數組替換舊數組:
example1.items = example1.items.filter(function (item) { return item.message.match(/Foo/) })
你可能認為這將導致 Vue 丟棄現有 DOM 並重新渲染整個列表。幸運的是,事實並非如此。Vue 為了使得 DOM 元素得到最大範圍的重用而實現了一些智能的、啟發式的方法,所以用一個含有相同元素的數組去替換原來的數組是非常高效的操作。
- 註意事項
由於 JavaScript 的限制,Vue 不能檢測以下變動的數組:
- 當你利用索引直接設置一個項時,例如:vm.items[indexOfItem] = newValue
- 當你修改數組的長度時,例如:vm.items.length = newLength
var vm = new Vue({ data: { items: [‘a‘, ‘b‘, ‘c‘] } }) vm.items[1] = ‘x‘ // 不是響應性的 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, key, value) 方法向嵌套對象添加響應式屬性。例如,對於:
var vm = new Vue({ data: { userProfile: { name: ‘Anika‘ } } })
你可以添加一個新的 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: ‘Vue Green‘ })
你應該這樣做:
vm.userProfile = Object.assign({ }, vm.userProfile, { age: 27, favoriteColor: ‘Vue Green‘ })
顯示過濾 / 排序結果
- 有時,我們想要顯示一個數組的過濾或排序副本,而不實際改變或重置原始數據。
-
在這種情況下,可以創建返回過濾或排序數組的計算屬性。
例如:
<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 循環中) 你可以使用一個 method 方法:
<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 9 10
-
v-for on a <template>
類似於 v-if,你也可以利用帶有 v-for 的 <template> 渲染多個元素。比如:
<ul> <template v-for="item in items"> <li>{{ item.msg }}</li> <li class="divider"></li> </template> </ul>
- v-for with v-if
<ul v-if="todos.length"> <li v-for="todo in todos"> {{ todo }} </li> </ul> <p v-else>No todos left!</p>
------- end -------
Vue.js(五)列表渲染 v-for