Vue 3 v-for 列表渲染完全指南:高效渲染與 key 值的效能優化關鍵
在前端開發中,動態渲染資料列表是最常見的需求之一。Vue 3 提供了直覺且強大的 v-for 指令,讓我們能輕鬆地將陣列或物件渲染為 DOM 元素。
然而,許多開發者在處理動態列表時,常因忽略了 :key 的重要性或錯誤地將 v-for 與 v-if 混用,進而引發網頁效能瓶頸。本文將從基礎語法出發,逐步帶您掌握 Vue 3 列表渲染的實戰技巧與底層優化原理。
v-for 的基本語法
v-for 指令需要使用 item in items 的特殊語法,其中 items 是來源資料,而 item 則是反覆運算過程中的別名。
遍歷陣列
您可以只取得陣列的元素,也可以同時取得它的索引值 index。
const products = reactive([
{ id: 101, name: "無線滑鼠", price: 890 },
{ id: 102, name: "機械鍵盤", price: 2490 },
{ id: 103, name: "27吋螢幕", price: 5900 },
]);<ul>
<li v-for="p in products" :key="p.id">{{ p.name }} - ${{ p.price }}</li>
<li v-for="p, index in products" :key="p.id">
項目 {{ index + 1 }}: {{ p.name }}
</li>
</ul>遍歷物件
除了陣列,v-for 也能用來遍歷物件的屬性,它支援最多三個參數:value(值)、key(鍵名)與 index(索引):
const userProfile = reactive({
name: "Away",
role: "Frontend Developer",
location: "Taiwan",
});<ul>
<li v-for="(value, key, index) in userProfile" :key="key">
[{{ index }}] {{ key }}: {{ value }}
</li>
</ul>v-for 必須綁定 :key
在撰寫 v-for 時,編輯器通常會強制要求綁定 :key。這不僅是為了通過 ESLint 檢查,更是 Vue 底層虛擬 DOM(Virtual DOM)Diff 演算法的效能核心。
:key 的底層原理
當資料列表發生變更,例如:插入、刪除或排序時,Vue 會透過 :key 作為節點的唯一識別碼。
- 有綁定唯一值:Vue 能夠精準辨識出哪個節點被移動、刪除或新增,並就地重用未改變的 DOM 元素,達成極高渲染效能。
- 沒綁定或使用
index作為 key:當列表順序打亂時,Vue 會被迫採取就地更新的策略,將所有 DOM 節點重新擦除並填入新資料。這不僅消耗效能,還可能導致表單輸入狀態,例如:<input>發生錯亂。
| 語法 | 缺點 / 優點 |
|---|---|
v-for="(item, index) in list" :key="index" | 列表順序反轉或中間插入資料時,依然會引發全面重繪 |
v-for="item in list" :key="item.id" | 完美的唯一識別,Diff 演算法效率最大化 |
效能地雷:v-for 與 v-if 同時使用
在 Vue 2 中,v-for 的優先級高於 v-if;但在 Vue 3 中,v-if 的優先級高於 v-for。
這意味著如果您把兩者寫在同一個標籤上,v-if 執行的當下還拿不到 v-for 的變數,進而引發錯誤,例如:Property "item" is not defined。
<!-- 錯誤使用方式 -->
<li v-for="item in list" v-if="item.isActive" :key="item.id">
{{ item.name }}
</li>使用 computed
先在 computed 中把資料過濾好,再讓 v-for 進行渲染。這樣既乾淨、效能又高,因為避免了每次渲染都重複做條件判斷。
const list = reative([
{ id: 1, name: "A", isActive: true },
{ id: 2, name: "B", isActive: false },
]);
// 在 JavaScript 端先過濾好
const activeItems = computed(() => list.filter((each) => each.isActive));<ul>
<li v-for="item in activeItems" :key="item.id">{{ item.name }}</li>
</ul>使用 template 標籤
如果您只是想要切換整個列表的顯示狀態,而非過濾內層元素,可以使用不具備實體 DOM 節點的 <template> 作為容器。
<ul>
<template v-for="item in items" :key="item.id">
<li v-if="item.isActive">{{ item.name }}</li>
</template>
</ul>控制整個列表是否顯示
如果是要控制整條 <ul> 列表要不要出現,應該要把 v-if 放在最外層,裡面再正常跑 v-for。
<ul v-if="items.length">
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>陣列變更偵測與解構賦值
陣列變更偵測
Vue 3 能夠自動偵測到響應式陣列被修改並同步更新畫面。以下方法會觸發畫面更新:
- 兩側端點操作:
push(),pop(),unshift(),shift()。 - 內部結構與順序:
splice(),sort(),reverse()。
如果您使用的是非變更方法,例如:map()、filter()、concat()、slice(),它們會回傳一個新陣列,此時您只需要用新陣列覆蓋舊陣列即可。
// 觸發 Vue 響應式更新
items.value = items.value.filter((item) => item.price > 100);在 v-for 中使用解構賦值
為了讓模板語法碼更簡潔,您可以在 v-for 中直接對物件進行解構。
<li v-for="{ id, name, price } in products" :key="id">
商品:{{ name }} | 售價:{{ price }}
</li>總結:v-for 列表渲染
熟練掌握 Vue 3 的 v-for 列表渲染是前端基本功,但在邁向資深開發者的路上,務必謹記以下兩點:
- 只提供唯一的
:key,避免偷懶使用index。 - 絕不把
v-for與v-if寫在同一個標籤上,優先使用 computed 處理乾淨的資料來源。
