當數據類型是陣列或物件時,我們不會希望將重複的選項或元素直接硬編碼在模板中。這樣的寫法會導致代碼冗長且不具可維護性。
這時候就會使用v-for
指令,在模板中可以根據陣列或物件的特性,動態地迴圈渲染每個選項。
⭐ 在使用v-for
指令時,需要為元素綁定一個唯一的key
,key
建議使用基礎型別的值
,且不要使用索引作為 key,以確保渲染效率和狀態一致性。
以下透過印出產品清單案例解釋使用v-for
與不使用v-for
的差別:
👉 Vue3 Options API v-for 陣列基本應用實作連結
未使用v-for
指令,只能一個一個條列出想顯示的項目:
Vue Template:
<ul>
<li>{{ productList[0] }}</li>
<li>{{ productList[1] }}</li>
<li>{{ productList[2] }}</li>
</ul>
使用v-for
指令,免除重複冗長的程式碼:
Vue Template:
<ul>
<li v-for="item in productList">
{{ item }}
</li>
</ul>
JavaScript:
const rootComponent = {
data() {
return {
productList: ["漢堡", "熱狗", "法式吐司"]
};
}
};
迭代項目
、索引值
。屬性值
、屬性名稱
、索引值
。👉 Vue3 Options API v-for 指令參數介紹實作連結
Vue Template
<div class="card">
<h2>陣列綁定使用(參數:迭代項、索引值)</h2>
<!-- 按照索引順序 -->
<ul>
<li v-for="(item, index) in likeFood">item:{{ item }} - index:{{ index}}</li>
</ul>
</div>
<div class="card">
<h2>物件綁定使用(參數:屬性值、屬性名稱、索引值)</h2>
<!-- 按照 Object.values() 順序 -->
<ul>
<li v-for="(value, key, index) in author">value:{{ value }} - key:{{ key }} - index:{{ index }}</li>
</ul>
</div>
JavaScript:
const rootComponent = {
data() {
return {
likeFood: ["頻果", "芭樂", "香蕉"],
author: { id: 0, name: "John", age: 24 }
};
}
};
當使用v-for
指令時,Vue 採用一種稱為「就地更新」(in-place patch)
的策略。這意味著當數據變更時,Vue 會優先只更新實際改變的部分,而不會重新創建或移動 DOM 元素。這種策略能有效減少不必要的 DOM 操作,從而顯著提升渲染效能。
然而,這種策略僅適用於列表項目本身不依賴內部臨時狀態
的情況。如果列表項目中包含需要保留的臨時狀態(例如表單輸入值或暫存數據),則必須使用 key 屬性來確保元素能正確更新與渲染。
以下分別透過表單輸入值跟核取方塊(Checkbox)臨時狀態舉例說明:
⭐ 特別注意這兩個範例都沒有在v-for
指令使用的元素上綁定key
屬性
核取方塊(Checkbox)殘留臨時狀態的案例:
👉 Vue3 Options API v-for 指令臨時性狀態(checkbox) 未綁定 key 屬性異常實作連結
流程說明:點選第一個家事或第二個買菜,當陣列順序發生改變的時候,臨時狀態會殘留在上面。
發生此異常的原因如下:
由於 Vue 的就地更新策略,導致事件 C
的內容被替換至事件 B
,事件 B
的內容則替換到事件 A
上。這使得原本事件 A
的勾選狀態被保留並錯位到新的內容上,導致勾選的臨時狀態與內容不匹配,最終出現不符合預期的情況。
解決方式:在v-for
指令的元素上,使用唯一值id
綁定key
屬性。
Vue Template:
<div class="pendingList">
<h2>代辦清單</h2>
<ul>
<li v-for="(data, index) in pendingList">
事件:{{ data.name }} - 時間:{{ data.time }} 分鐘
<label><input type="checkbox" v-model="data.isDone"></label>
</li>
</ul>
</div>
<div class="completedList">
<h2>已完成清單</h2>
<ul>
<li v-for="(data, index) in downList">
事件:{{ data.name }} - 時間:{{ data.time }} 分鐘
<label><input type="checkbox" v-model="data.isDone"></label>
</li>
</ul>
</div>
JavaScript:
const rootComponent = {
data() {
return {
todoList: [
{
id: 0,
name: "做家事",
time: 20,
isDone: false
},
{
id: 1,
name: "買菜",
time: 40,
isDone: false
},
{
id: 2,
name: "看書",
time: 60,
isDone: false
}
]
};
},
computed: {
pendingList() {
return this.todoList.filter((item) => item.isDone === false);
},
downList() {
return this.todoList.filter((item) => item.isDone !== false);
}
}
};
表單輸入值(input)殘留臨時狀態的案例:
👉 Vue3 Options API v-for 表單輸入框輸入狀態未綁定 key 屬性異常實作連結
流程說明:
由於未綁定key
屬性,當項目順序變更時,Vue 只會替換元素的內容,導致輸入框的臨時狀態被保留並錯位,從而產生與預期不符的顯示結果。
解決方式:在v-for
指令的元素上,使用項目值
綁定key
屬性。
Vue Template:
<div class="container">
<h3>v-for 未加上key的使用</h3>
<ul>
<li v-for="(item, index) in productList">
(未加上key)索引值:{{ index }} - 產品名稱:{{ item }}
<input type="text">
</li>
</ul>
<button type="button" @click="reverseArray()">陣列反轉按鈕</button>
</div>
JavaScript:
const rootComponent = {
data() {
return {
productList: ["漢堡", "熱狗", "法式吐司"]
};
},
methods: {
reverseArray() {
this.productList.reverse();
}
}
};
Vue3 中 v-if
優先級大於 v-for
。同時使用在同一元素是非常不建議的。
Vue Template:
<li v-for="item in likeFood" v-if="item !== '蘋果'">
{{ item }}
</li>
由於v-if
的優先級較高,會導致在條件判斷之前無法正常讀取 item 的值,從而導致 Vue 拋出警告,並影響渲染結果。
解決方式可以透過<template>
包裹元素在外層,進而避免v-if
及v-for
指令同時在一個元素上使用。
👉 Vue Options API template 及計算屬性搭配 v-for 基本使用實作連結
HTML:
<template v-for="item in likeFood">
<li v-if="item !== '蘋果'">
{{ item }}
</li>
</template>
雖然這裡可以使用template 標籤
來處理,但這種方式看起來沒有預期的那麼直觀和易於理解。由於框架的核心特性是數據驅動,另一種更優的方法是通過預先處理數據,並結合Vue 指令來簡化邏輯,從而減少模板的複雜度。
接下來,我們可以嘗試使用計算屬性來進行改寫:
Vue Template:
<li v-for="item in getFilterFood">
{{ item }}
</li>
javaScript:
computed: {
getFilterFood() {
return this.likeFood.filter((item) => item !== "蘋果");
}
}
這邊我有準備另一個更適合使用template
標籤的案例。
表格渲染區塊
要顯示不同內容:
👉 Vue3 Options API v-for 表格搭配 Template 使用實作連結
流程說明:將商品列表用表格方式呈現。如果有商品額外細節說明(EX:Very sour)表格要顯示,沒有則不用。
HTML:
<table>
<thead>
<tr>
<th>id</th>
<th>名稱</th>
<th>價格</th>
</tr>
</thead>
<tbody>
<template v-for="item in productList">
<tr>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.price }}</td>
</tr>
<tr v-if="item.detail">
<td colspan=3>{{ item.detail.msg }}</td>
</tr>
</template>
</tbody>
</table>
javaScript:
const rootComponent = {
data() {
return {
productList: [
{
id: 1,
name: "orange",
price: 50,
detail: { msg: "Very sour " }
},
{
id: 2,
name: "banana",
price: 30
}
]
};
}
}
除了用來迭代陣列和物件之外,v-for 指令還可以直接用於純數字的迭代。這樣的用法可以用來生成特定次數的元素。
流程說明:生成固定數量的元素。
👉 Vue3 Options API v-for 純數字迭代使用實作連結
<div id="app">
<ul>
<li v-for="n in 10">
數字:{{ n }}
</li>
</ul>
</div>
當面對雙層陣列的數據時,若需要從不同陣列中進行篩選,可以透過v-for
指令結合方法來實作。
但是在操作數據時,要特別小心避免直接修改原始數據(例如使用sort
或reverse
會改變元數據)。若必須使用這些方法,建議先對數據進行拷貝
再操作,以避免潛在的副作用。
流程說明:
v-for
指令迭代第一層陣列,取得每個子陣列。👉 Vue3 Options API v-for 雙迴圈搭配方法實作連結
HTML:
<div id="app">
<ul>
<li v-for="(product, index) in productList" :key="product">
第{{ index }}個陣列產品清單中便宜商品:
<p v-for="cheapProduct in getCheapProducts(product)">
{{ cheapProduct}}
</p>
</li>
</ul>
</div>
javaScript:
const rootComponent = {
data() {
return {
productList: [
[
{ id: 1, name: "滑鼠", price: 100 },
{ id: 2, name: "鍵盤", price: 350 },
{ id: 3, name: "電腦", price: 250 }
],
[
{ id: 4, name: "耳機", price: 200 },
{ id: 5, name: "音響", price: 400 },
{ id: 6, name: "麥克風", price: 650 }
]
]
};
},
methods: {
getCheapProducts(productArray) {
// 需避免使用影響元數據的方法(filter 本身就會回傳新的陣列)
return productArray.filter((item) => item.price < 350);
}
}
}
迭代項
及索引值
。屬性值
、屬性名稱
及索引值
。迭代數字範圍
,生成指定次數的元素。v-for
中使用key
屬性,能夠確保元素的唯一性
,避免因為就地更新策略導致的錯誤渲染(特別是臨時狀態的判定)。v-if
的優先級高於v-for
,因此應避免在同一元素上同時使用,這會導致元素在條件判斷之前無法讀取v-for
迭代項的值。若需要使用可以搭配template
標籤使用。v-for
指令可以與自定義方法
結合使用進行篩選。需注意避免對元數據的直接操作,應對數據進行拷貝
後再進行處理。使用今天學習到的v-for
指令,試著將雙層陣列的基數給印出來。(僅需調整模板的部分搭配使用v-for
指令即可)
👉 Vue3 Options API 基數數字練習模板連結
👉 Vue3 Options API 基數數字完成實作連結