1. 陣列與物件 v-for 的索引不同

v-for 使用在陣列與物件上,會有哪些差異呢?

  • 使用於陣列上 → 索引 (key) 為 0, 1, 2
<li v-for="(item, key) in arrayData">
  {{ key }} - {{ item.name }} {{ item.age }} 歲
</li>
  • 使用於物件上 → 索引 (key) 為物件的屬性
<li v-for="(item, key) in objectData">
  {{ key }} - {{ item.name }} {{ item.age }} 歲
</li>

GITHUB

2. 就地更新(快速置換)

先在 <input> 上按照順序輸入 1 2 3,接著按下按鈕反轉陣列,發現 1 2 3 的順序沒有改變,為什麼呢?

<li v-for="(item, key) in arrayData">
  {{ key }} - {{ item.name }} {{ item.age }} 歲
  <input type="text" />
</li>
<button class="btn" @click="reverseArray">反轉陣列</button>
// methods 裡的 reverseArray
reverseArray: function () {
    this.arrayData.reverse();
    console.log(this.arrayData);
},

情況如下圖,到底是什麼原因,讓我們執行反轉陣列後,三個 Input 的順序卻沒有跟著反轉呢?

GITHUB

原因是因為 Vue 替換 DOM(文件物件模型)元素是使用就地更新(快速置換)的方式。

原理解釋

當 Vue 正在更新使用 v-for 渲染的元素列表時,它默認會使用就地更新的策略。

在就地更新的狀況下,當資料的順序被改變,Vue 不會因應資料順序而移動 DOM 元素,而是會就地更新每個元素的內容,並且確保它們在每個索引位置上正確渲染。

如果不想就地更新,希望要達到反轉,那我們需要給 Vue 提示,讓它能追蹤每個節點的身份,才能既重複使用,又正確地重新排序元素,而這個提示就是為每一項元素加上唯一的 key 值。

加上理想的 key 值

  • 使用 :key 是為了避免就地複用(快速替換),我們想要直接做強制替換
  • 理想的 key 值是每項都有的唯一 ID,重複值是不能用來作為 key 的

在這個例子中,使用 item.ageitem.name 作為 key 都是可以的,但是不能用 key(也就是 :key = "key")。
為什麼不行?這邊 key 不是剛好分別是 0, 1, 2 嗎?

這是因為如果用 key 當作唯一值,則在反轉前與反轉後,這些 key 在順序上依然都是 0, 1, 2,而不是變成 2, 1, 0,結果就會變成就地更新的情況了。

所以撰寫的時候,如果有唯一的 ID 的值可以使用,就盡量不要使用 index 這種索引值。

最後提醒一點,官方建議使用 v-for 都綁定 key 喔

3. 過濾(Filter)

使用 v-for 的時候,常常會使用到過濾。

例如:針對上方輸入的文字 (filterText),過濾出下方的內容 (filterArray)

<input type="text" v-model="filterText" @keyup.enter="filterData" />
<ul>
  <li v-for="(item, key) in filterArray" :key="item.age">
    {{ key }} - {{ item.name }} {{ item.age }} 歲 <input type="text" />
  </li>
</ul>

這裡的 filterArray 是一個空陣列

// methods 裡的 filterData
filterData: function () {
    var vm = this; // 這個 this 指向 Vue 應用程式
    vm.filterArray = vm.arrayData.filter(function (item) {
        console.log(vm.filterText, item.name, item.name.match(vm.filterText));
        return item.name.match(vm.filterText);
    });
},

match() 說明

match() 主要用途是核對字串是否相符。
(這裡是拿 item.namevm.filterText 比較是否相符)

Array.prototype.filter() 說明

filter()forEach() 很像,都是用來處理陣列資料,會將陣列內的每個元素一個個傳入並執行給定的函式一次。
然而 filter() 多了一個 return。
return 後面的內容是 true 的話,便會回傳「陣列內的 單個元素」到指定的變數內。

此段說明參考 JavaScript 陣列處理方法
filter() 會回傳一個陣列,其條件是 return 後方為 true 的物件,很適合用在搜尋符合條件的資料。

在這個例子中:
這裡的 item 代表 arrayData 內的每一個元素(e.g.{name: '小明', age: 16}

使用 match() 比較 item.namevm.filterText 是否相符,
相符便回傳 arrayData 內的單個元素(e.g.{name: '小明', age: 16})到變數 filterData 內。

4. 不能運行的狀況

設長度為零

在原本的 JavaScript 裡,如果把陣列長度改成 0 的話,可以清空陣列。

// methods 裡的 cantWork
cantWork: function () {
    this.arrayData.length = 0;
    console.log(this.arrayData);
}

但是在 Vue 裡面,這樣的操作是沒有用的。
雖然 arrayData 的長度確實會被改成零,但資料還是存在。

直接操作陣列

直接指定陣列裡的第 0 個物件並修改資料,雖然在 console 與 Vue 開發者工具中會看到更改,但是畫面資料卻沒有跟著變動。

// methods 裡的 cantWork
cantWork: function () {
    this.arrayData[0] = {
        name: '小強',
        age: 99,
    }
    console.log(this.arrayData);
}

在 Vue 中,如果要操作陣列裡的內容,不能透過索引去操作它。
我們必須使用 Vue.set()

解法:Vue.set(target, key, value)

想要操作或新增「原本沒有在 data 裡的資料」時,需要使用 Vue.set(針對目標, 索引, 值) 強制將資料寫入 data。

// methods 裡的 cantWork
cantWork: function () {
    Vue.set(this.arrayData, 0, {
        name: '阿強',
        age: 99
    });
    console.log(this.arrayData);
}

5. 純數字的迴圈

除了使用陣列或物件,也可以使用純數字的迴圈。

<ul>
  <li v-for="item in 10">
    {{ item }}
  </li>
</ul>

使用 v-for="item in 一個數值",數值是多少,迴圈就會跑幾次。

GITHUB

6. 在 Template 上運用 v-for

如果需要同時使用 v-forv-if 的話,可以藉由在 <template> 標籤上使用 v-for,唯一值 key 綁定在內部元素上的方式。

注意!雖然在官方文件的範例 v-for on a template 當中並沒有綁定 key,但我認為還是應該加上 key,目的是讓 Vue 在演算上能夠區分出元素的唯一性,因此我一樣會做綁定 key 的動作。

<ul>
  <template v-for="item in items">
    <li :key="item.uid" v-if="true">{{ item.msg }}</li>
    <li class="divider" role="presentation"></li>
  </template>
</ul>

7. v-for 與 v-if 混合使用

使用 v-ifv-for 加入年齡限制,改變畫面上呈現的結果。

<ul>
  <li v-for="(item, key) in arrayData" v-if="item.age<=20">
    {{ key }} - {{ item.name }} {{ item.age }} 歲
  </li>
</ul>

GITHUB

8. v-for 與元件

這邊簡單介紹元件與 v-for 的運用,之後會再詳細介紹元件。

因為現在建議元件使用 v-for 都加上 key,
所以我們想要在元件 list-item 上加入剛才講到的 key 的話,
我們要先定義好元件,接著再加上唯一的值 (ID) 當作 key。

這邊使用的 item.age 就是唯一的值。

<list-item
  :item="item"
  v-for="(item, key) in arrayData"
  :key="item.age"
></list-item>
Vue.component('list-item', {
  template: `
    <li>
      {{ item.name }} {{ item.age }} 歲
    </li>
  `,
  props: ['item'],
});

以上資源是我自己整理過後的筆記,若有錯誤歡迎隨時和我聯繫