Vue.js: 列表渲染 v-for

v-for

使用v-for迭代陣列或物件中的元素。

例 1:陣列

使用v-for迭代陣列中的元素。如下所示,list 是一個陣列,item 代表用於迭代的元素,使用item.iditem.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 以顯示完整程式碼。

渲染結果如下。

Vue.js: 列表渲染 v-for

Vue.js: 列表渲染 v-for

例 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' }
    }
  }
});

渲染結果如下。

Vue.js: 列表渲染 v-for

Vue.js: 列表渲染 v-for

例 3:使用常數迭代

<div>
  <span v-for="n in 10">${ n }</span>
</div>

渲染結果如下。

Vue.js: 列表渲染 v-for

例 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' }
    ]
  }
});

初始畫面,使用者分別在每個輸入框中輸入文字。

Vue.js: 列表渲染 v-for

Vue.js: 列表渲染 v-for

使用vm.list.reverse()改變元素順序後,雖然元素被更新,但使用者的輸入被保留,這是因為元素並沒有被重新渲染,而只是部份更新而已。

Vue.js: 列表渲染 v-for

Vue.js: 列表渲染 v-for

修改上例,每個<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 值便會重新更新,如下,由於第一個和第三個元素順序改變,因此被重新渲染。

Vue.js: 列表渲染 v-for

陣列操作

如上所示,使用vm.list.reverse()改變元素順序-反序排列,其他操作陣列的 method 還有:

顯示過濾 / 排序結果

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
      });
    }
  }
});

Vue.js: 列表渲染 v-for - 顯示過濾 / 排序結果

v-forv-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-for 的個數範圍判斷條件成立後,才會輪到 v-if 來判斷顯示與否。這是什麼意思呢?來看下一個例子就知道了。

如下所示,這裡同時出現了 v-forv-ifv-else 三個指令。在這個例子中,我們試圖印出 list 陣列的元素內容,而目前 list 是空陣列,沒有任何項目可顯示。因此,當 v-for 在判斷 item in list 時,發現條件不成立,就不去做 v-ifv-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: []
  }
});

看完整範例與 Demo

解法如下,讓 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>

看完整範例與 Demo

總結,由於 v-for 優先權較高,當 v-for 執行完條件判斷後,若條件不成立,則後面的 v-if 也不會執行,接著忽略了其後的 v-else

以上參考自 List Rendering


comments powered by Disqus