iT邦幫忙

2025 iThome 鐵人賽

DAY 13
0
Vue.js

在 Vue 過氣前要學的三十件事系列 第 13

在 Vue 過氣前要學的第十三件事 - Here we go again / v-for

  • 分享至 

  • xImage
  •  

前言

在前端開發中,難免動態決定該渲染什麼資料的情況,
舉例來說,今天有一個畫面是 :

要根據 API 回傳的資料來渲染該公司的員工列表

那公司員工這個資料可能不是固定的,因此不太會透過靜態寫死的方式去傳遞,
這個時候就很好的可以使用 v-for 來完成動態渲染的功能了。

使用

基礎語法

<div v-for="(item, index) in items" :key="index"></div>

v-for 基本語法範例1
複數 items 代表你要動態渲染的來源
單數 item 代表你每次迭代所產生的項目

<script setup>
  const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
</script>

<template>
  <ul>
    <li v-for="item in items" :key="item.name">{{ item.name }}</li>
  </ul>
</template>

v-for 基本語法範例2

以上面為例,v-for 傳入一個值,
這個值代表每次迭代會從 items 中取出一個 item

而後面的 {{}} 就可以放入我們想動態渲染的值,
例如 item 裡面的 name

這邊有兩點要注意。

key 一定要加嗎? 貌似不加也能跑


key 屬性是為了讓 Vue 能正確追蹤每個節點,
建議還是要加上去,而且還要具有唯一性

舉例來說,上面的 :key="item.name,就是一個反面教材
因爲名字的重複性實在太高了,

<li v-for="(item, index) in items" :key="index">{{ item.name }}</li>

key 屬性使用

因此可以考慮加上 index 作為 key 值。

注意:當你這個動態渲染的值是有可能修改 (新增,刪除,etc.),那就不建議使用 index
因為 v-for 的更新渲染是基於 "in-place patch" 策略,也就是說,

如果真的沒有一個適合的唯一值,又有可能會對內容作修改,
這邊有一個解決方案:利用模板語法進行拼接

如果連這個都有可能重複,那你可能要考慮資料格式是否有需要調整的地方,例如新增確切的唯一值

  <li v-for="(item, index) in items" :key="`${item.message}-${index}`">
      {{ item.message }}
  </li>

模板語法拼接範例

只能傳 Array 嗎?

https://ithelp.ithome.com.tw/upload/images/20250912/20172784oOyjKWje1J.jpg
v-for 的期望值是能接受蠻多種類型的:

  • number 如果是數字其實蠻有趣的,他會把這個值視為 一個範圍 ,像下面這個就是 1~3。
<span v-for="n in 3">{{ n }}</span>
<!-- 要注意 n 的初始值是從 1 開始算 -->

https://ithelp.ithome.com.tw/upload/images/20250912/20172784bczXhpVrZb.png

  • string 這個就更有趣了,如果是字串,就會把你的字拆開來當作 array 迭代。
<li v-for="char in 'ithome'">{{ char }}</li>

  • Array
const nums = ref([1, 2, 3, 4, 5]);
<li v-for="num in nums">{{ num }}</li>
  • Object
const myObject = ref({
  title: "在 Vue 過氣前要學的三十件事",
  author: "Yanya",
  publishedAt: "2025/09/01",
});
<li v-for="value in myObject">{{ value }}</li>

傳入物件有趣的地方在於你一開始所迭代的值預設是 value
但你其實可以取出三個參數,按順序分別是 value,key,index

<li v-for="(value, key, index) in myObject">
      {{ index }}. {{ key }} {{ value }}
</li>

v-for Object 三個參數

  • Iterable (Set, Map, Generator…)
<script setup>
  const mySet = new Set([1, 2, 3]);
  const myMap = new Map([
    ["a", 1],
    ["b", 2],
  ]);
  // const myGo = "一輩子";
</script>

<template>
    <li v-for="val in mySet">{{ val }}</li>
    <li v-for="[key, val] in myMap">{{ key }}: {{ val }}</li>
</template>

原始碼

不知道各位有沒有寫過 for loop

for (let i = 0; i < 5; i++) {
  console.log(i)
}

for loop 範例

其實如果我們打開 v-for 的原始碼來看就可以發現,
他本質上都是把我們傳入的 value 透過 new Array() / Array.from() / Object.keys()
轉換成Array,然後對這個 Array 做 for loop,
每次 loop 就做 renderItem() 的動作。

// node_modules>@vue>runtime-core>dist>runtime-core.cjs.js
function renderList(source, renderItem, cache, index) {
  // 略
  if (sourceIsArray || shared.isString(source)) {
    // 略
    ret = new Array(source.length);
    for (let i = 0, l = source.length; i < l; i++) {
      ret[i] = renderItem(
        // 略
      );
    }
  } else if (typeof source === "number") {
    // 略
    ret = new Array(source);
    for (let i = 0; i < source; i++) {
      ret[i] = renderItem(i + 1, i, void 0, cached && cached[i]);
    }
  } else if (shared.isObject(source)) {
    if (source[Symbol.iterator]) {
      ret = Array.from(
        source,
        (item, i) => renderItem(item, i, void 0, cached && cached[i])
      );
    } else {
      const keys = Object.keys(source);
      ret = new Array(keys.length);
      for (let i = 0, l = keys.length; i < l; i++) {
      // 略
   }

v-for 原始碼

v-memo

https://ithelp.ithome.com.tw/upload/images/20250912/20172784AuXzuLG9Um.jpg
這是一個 3.2 版本所推出的一個指令,主要透過緩存機制來優化大量表單渲染。

const list = ref([/* 1000 個以上的物件 */])
<div v-for="item in list" :key="item.id" v-memo="[item.id === selected]">
  <p>ID: {{ item.id }} - selected: {{ item.id === selected }}</p>
  <p>...more child nodes</p>
</div>

v-memo 範例程式碼
但這邊有一個事情要注意,由於 v-memo 的機制是,
當傳入值不變,那組件的更新將會被跳過,是直接連 Virtual DOM 都不會產生。

所以正確的指定緩存是必要的,
下面有個例子,可以看到我點擊不同卡片會有選取外框。

但當我不小心把 v-memo 傳入空依賴

<div
  v-for="item in items"
  :key="item.id"
  v-memo="[]"
  @click="setActive(item.id)"
>

v-memo 錯誤示範

他直接不更新我的畫面,

但實際上我確實有選到卡片,而眼尖的朋友也發現了,這其實效果就等同於 v-once

結語

今天我們帶到了非常常用的動態列表渲染指令 v-for,我們可以傳入什麼樣子的資料去做渲染,
帶到了 v-for 的原始碼讓各位可以更瞭解其背後的原理。

在大量列表渲染中可以使用 v-memo 來優化效能,
以及要注意可能會導致畫面停止更新的細節,其效果跟 v-once是一樣的。

明天我們將進入基礎語法的最後一篇文,事件綁定 v-on

如果你喜歡這個系列或是想看我發瘋,歡迎按下 訂閱 一起走完這三十天吧。
發現我垃圾話真的很多

一些小練習

  1. 什麼情況下是使用 v-for 的好時機?
  2. v-forkey 代表什麼,為什麼建議要加上這個屬性。
  3. v-for 可以接受哪幾種值,幫我舉三個例子。
  4. 什麼情況可以用 v-memo 來改善效能,要注意什麼情況下可能導致畫面停止更新。

https://ithelp.ithome.com.tw/upload/images/20250914/20172784echzNWoJmm.png


上一篇
在 Vue 過氣前要學的第十二件事 - 如果 IF / v-if
下一篇
在 Vue 過氣前要學的第十四件事 - 事件處理 / v-on
系列文
在 Vue 過氣前要學的三十件事15
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言