Skip to content

Vue 3 v-for 列表渲染完全指南:高效渲染與 key 值的效能優化關鍵

在前端開發中,動態渲染資料列表是最常見的需求之一。Vue 3 提供了直覺且強大的 v-for 指令,讓我們能輕鬆地將陣列或物件渲染為 DOM 元素。

然而,許多開發者在處理動態列表時,常因忽略了 :key 的重要性或錯誤地將 v-forv-if 混用,進而引發網頁效能瓶頸。本文將從基礎語法出發,逐步帶您掌握 Vue 3 列表渲染的實戰技巧與底層優化原理。

v-for 的基本語法

v-for 指令需要使用 item in items 的特殊語法,其中 items 是來源資料,而 item 則是反覆運算過程中的別名。

遍歷陣列

您可以只取得陣列的元素,也可以同時取得它的索引值 index

js
const products = reactive([
  { id: 101, name: "無線滑鼠", price: 890 },
  { id: 102, name: "機械鍵盤", price: 2490 },
  { id: 103, name: "27吋螢幕", price: 5900 },
]);
html
<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(索引):

js
const userProfile = reactive({
  name: "Away",
  role: "Frontend Developer",
  location: "Taiwan",
});
html
<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。

html
<!-- 錯誤使用方式 -->
<li v-for="item in list" v-if="item.isActive" :key="item.id">
  {{ item.name }}
</li>

使用 computed

先在 computed 中把資料過濾好,再讓 v-for 進行渲染。這樣既乾淨、效能又高,因為避免了每次渲染都重複做條件判斷。

js
const list = reative([
  { id: 1, name: "A", isActive: true },
  { id: 2, name: "B", isActive: false },
]);

// 在 JavaScript 端先過濾好
const activeItems = computed(() => list.filter((each) => each.isActive));
html
<ul>
  <li v-for="item in activeItems" :key="item.id">{{ item.name }}</li>
</ul>

使用 template 標籤

如果您只是想要切換整個列表的顯示狀態,而非過濾內層元素,可以使用不具備實體 DOM 節點的 <template> 作為容器。

html
<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

html
<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(),它們會回傳一個新陣列,此時您只需要用新陣列覆蓋舊陣列即可。

js
// 觸發 Vue 響應式更新
items.value = items.value.filter((item) => item.price > 100);

在 v-for 中使用解構賦值

為了讓模板語法碼更簡潔,您可以在 v-for 中直接對物件進行解構。

js
<li v-for="{ id, name, price } in products" :key="id">
  商品:{{ name }} | 售價:{{ price }}
</li>

總結:v-for 列表渲染

熟練掌握 Vue 3 的 v-for 列表渲染是前端基本功,但在邁向資深開發者的路上,務必謹記以下兩點:

  • 只提供唯一的 :key,避免偷懶使用 index
  • 絕不把 v-forv-if 寫在同一個標籤上,優先使用 computed 處理乾淨的資料來源。