Vue.js: 列表渲染 v-for
27 Apr 2017v-for
使用 v-for
迭代陣列或物件中的元素。
例 1:陣列
使用 v-for
迭代陣列中的元素。如下所示,list 是一個陣列,item 代表用於迭代的元素,使用 item.id
或 item.name
可帶出屬性。其中第二個參數 index 是索引值 (optional)。
<div id="app">
<ul>
<li v-for="(item, index) in list">
index: ${ index }, name: ${ item.name }
</li>
</ul>
</div>
var vm = new Vue({
el: '#app',
delimiters: ['${', '}'],
data: {
list: [
{ id: '123456789', name: '選項 1' },
{ id: '234567890', name: '選項 2' },
{ id: '345678901', name: '選項 3' },
],
},
});
// 由於部落格會把使用雙花括號的內容吃掉,所以設定 delimiters 以顯示完整程式碼。
渲染結果如下。
例 2:物件
使用 v-for
迭代物件中的元素。第二個參數 key 是鍵值,第三個參數 index 是索引值,皆為 optional。
<div id="app">
<ul>
<li v-for="(item, key, index) in list">
index: ${ index }, key: ${ key }, name: ${ item.name }
</li>
</ul>
</div>
var vm = new Vue({
el: '#app',
delimiters: ['${', '}'],
data: {
list: {
'123456789': { name: '選項 1' },
'234567890': { name: '選項 2' },
'345678901': { name: '選項 3' },
},
},
});
渲染結果如下。
例 3:使用常數迭代
<div>
<span v-for="n in 10">${ n }</span>
</div>
渲染結果如下。
例 4:<template>
使用 <template>
標籤。
<div id="app">
<ul>
<template v-for="(item, index) in list">
<li>index: ${ index }, name: ${ item.name }</li>
</template>
</ul>
</div>
例 5:元件 Component
可參考 Todo List 的 HTML 部份 和 JavaScript 部份。
key
由於效能考量,在預設的狀況下,Vue.js 會儘量重覆使用已渲染好的元素。
若不設定 key 值,不會重新渲染元素,只會部份更新。
<div id="app">
<ul>
<li v-for="(item, index) in list">${ index } <input type="text" :placeholder="item.name" /></li>
</ul>
</div>
var vm = new Vue({
el: '#app',
delimiters: ['${', '}'],
data: {
list: [
{ id: '123456789', name: '選項 1' },
{ id: '234567890', name: '選項 2' },
{ id: '345678901', name: '選項 3' },
],
},
});
初始畫面,使用者分別在每個輸入框中輸入文字。
使用vm.list.reverse()
改變元素順序後,雖然元素被更新,但使用者的輸入被保留,這是因為元素並沒有被重新渲染,而只是部份更新而已。
修改上例,每個 <li>
都使用 v-bind
綁定一個屬性 :key
並設定唯一值,目的是確保每個元素的唯一性,當元素更新,例如改變順序時,有可識別唯一性的 key 來確保重新渲染。
<div id="app">
<ul>
<li v-for="(item, index) in list" :key="item.id">
${ index } <input type="text" :placeholder="item.name" />
</li>
</ul>
</div>
var vm = new Vue({
el: '#app',
delimiters: ['${', '}'],
data: {
list: [
{ id: '123456789', name: '選項 1' },
{ id: '234567890', name: '選項 2' },
{ id: '345678901', name: '選項 3' },
],
},
});
設定 key 值便會重新更新,如下,由於第一個和第三個元素順序改變,因此被重新渲染。
陣列操作
如上所示,使用 vm.list.reverse()
改變元素順序-反序排列,其他操作陣列的 method 還有:
push()
:新增元素。pop()
:刪除最新加入的元素。shift()
:刪除第一個 (即最舊的) 元素。unshift()
:加入元素至第一個位置。splice()
:加入或移除元素。sort()
:由小至大排序。reverse()
:元素反序排列。filter()
:過濾陣列的元素,並將符合條件的元素傳回成為一個新陣列。concat()
:連接陣列,會返回一個新的陣列。slice()
:切割陣列,會返回一個新的陣列。
顯示過濾 / 排序結果
v-for
迭代的資料為使用 computed 或 methods 處理後的結果。例如:顯示數量大於 6 的水果。
<div id="app">
<ul>
<li v-for="item in filteredFrouts">${ item.name }</li>
</ul>
</div>
var vm = new Vue({
el: '#app',
delimiters: ['${', '}'],
data: {
frouts: [
{
name: 'Apple',
count: 10,
},
{
name: 'Orange',
count: 5,
},
{
name: 'Banana',
count: 20,
},
],
},
computed: {
filteredFrouts: function() {
return this.frouts.filter(function(item) {
return item.count > 6;
});
},
},
});
v-for
與 v-if
優先權的比較
v-for
的優先權高於 v-if
,因此當兩者皆出現在同一個元素上時,v-if
會隨著 v-for
重覆執行數次。如下所示,v-if
會執行 10 次,每次都會判斷 n 除以 2 的餘數是否為 1,若為 1 則顯示,否則就隱藏。
<ul>
<li v-if="n % 2 === 1" v-for="n in 10">${ n }</li>
</ul>
注意,v-for
的個數範圍判斷條件成立後,才會輪到 v-if
來判斷顯示與否。這是什麼意思呢?來看下一個例子就知道了。
如下所示,這裡同時出現了 v-for
、v-if
和 v-else
三個指令。在這個例子中,我們試圖印出 list 陣列的元素內容,而目前 list 是空陣列,沒有任何項目可顯示。因此,當 v-for
在判斷 item in list
時,發現條件不成立,就不去做 v-if
和 v-else
判斷了,導致原本沒有元素印出時應該要提示的「Nothing could show.」都沒有出現 :(
<div id="app">
<div v-if="list.length !== 0" v-for="item in list">
<div>${ item.name }</div>
</div>
<div v-else>
Nothing could show.
</div>
</div>
new Vue({
el: '#app',
delimiters: ['${', '}'],
data: {
list: [],
},
});
解法如下,讓 v-for
來判斷 list 是否有內容的狀況,v-if
只要負責處理項目為 0 個的情況即可。
<div id="app">
<div v-for="item in list">
<div>${ item.name }</div>
</div>
<div v-if="list.length === 0">
Nothing could show.
</div>
</div>
總結,由於 v-for
優先權較高,當 v-for
執行完條件判斷後,若條件不成立,則後面的 v-if
也不會執行,接著忽略了其後的 v-else
。
以上參考自 List Rendering。