本文 AI 產出,尚未審核

Vue3 教學 – 模板語法:v-bind 綁定屬性


簡介

在 Vue 3 中,模板(Template) 是開發者與視圖互動的主要入口,而屬性綁定則是把資料從 JavaScript 端傳遞到 HTML 元素或子元件的關鍵機制。v-bind(縮寫為 :)正是完成這件事的核心指令,讓我們可以 動態條件式多屬性一次 地更新 DOM。

掌握 v-bind 的用法不僅能提升程式的可讀性與維護性,還能避免常見的錯誤(例如布林屬性被誤寫成字串),在實務開發中,無論是簡單的圖片切換、表單驗證,還是複雜的動態樣式、組件傳值,都離不開它。因此本篇將從概念到實作,完整說明 v-bind 的各種寫法與最佳實踐。


核心概念

1. 基本語法與縮寫

語法 說明
v-bind:attribute="expression" expression 的值綁定 attribute
:attribute="expression" v-bind 的縮寫,最常使用的寫法

範例:將 imgSrc 變數綁定到 <img>src 屬性

<img :src="imgSrc" alt="動態圖片">

2. 綁定布林(Boolean)屬性

HTML 的布林屬性(如 disabledreadonly)在寫成字串時會失去布林意義。使用 v-bind 時,只要表達式回傳 truefalse,Vue 會自動在真值時加入屬性、在偽值時移除屬性。

<button :disabled="isSubmitting">送出</button>

isSubmittingtrue 時,disabled 會被加入;為 false 時則不會出現在 DOM。

3. 動態屬性名稱(Dynamic Argument)

有時屬性的名稱本身也是變動的,Vue 允許使用方括號 [] 包住表達式作為 動態參數

<!-- attrName 可能是 'title'、'aria-label'、'data-id' 等 -->
<div :[attrName]="attrValue">{{ attrName }} = {{ attrValue }}</div>

4. 物件語法(Object Syntax)

一次綁定多個屬性時,使用 物件語法 可以讓程式碼更簡潔。物件的鍵是屬性名稱,值是對應的表達式。

<!-- 同時綁定 class、title、aria-hidden -->
<div v-bind="linkAttrs">連結</div>
export default {
  data() {
    return {
      linkAttrs: {
        class: 'btn primary',
        title: '前往首頁',
        'aria-hidden': false
      }
    }
  }
}

注意:物件鍵若含有連字符(-)需使用字串包住,或直接寫成字面量語法。

5. 陣列語法(Array Syntax)

對於 classstyle 綁定,陣列語法可以把多個條件式或計算結果組合在一起。

<!-- class 會根據條件自動合併 -->
<div :class="[baseClass, isActive ? 'active' : '', extraClass]"></div>

6. 綁定 classstyle 的特殊寫法

  • class:接受字串、陣列或物件。物件鍵為 class 名稱,值為布林條件。
  • style:接受字串、陣列或物件。物件鍵為 CSS 屬性(使用駝峰或 kebab-case),值為樣式值。
<!-- class 物件語法 -->
<div :class="{ disabled: isDisabled, highlighted: isHighlighted }"></div>

<!-- style 陣列語法 -->
<div :style="[baseStyle, dynamicStyle]"></div>
export default {
  data() {
    return {
      baseStyle: { color: 'blue', fontSize: '14px' },
      dynamicStyle: { backgroundColor: this.isActive ? 'lightgreen' : 'lightgray' }
    }
  }
}

7. 綁定子元件的 Prop

在自訂元件上使用 v-bind 時,會把表達式的值傳遞給元件的 props。若屬性名稱與 Prop 名稱不同(例如 kebab-case vs camelCase),Vue 會自動做對應。

<!-- 子元件 MyCard.vue 定義 props: { title: String, isActive: Boolean } -->
<MyCard :title="cardTitle" :is-active="isActive"></MyCard>

技巧:使用 v-bind="object" 可以一次把多個 prop 傳入子元件,省去逐一寫 : 的繁瑣。

<MyCard v-bind="cardProps"></MyCard>
export default {
  data() {
    return {
      cardProps: {
        title: 'Vue 3 入門',
        isActive: true,
        description: '學會 v-bind 後,你的元件會更靈活!'
      }
    }
  }
}

程式碼範例

以下提供 5 個實用範例,涵蓋常見需求與技巧,並附上註解說明。

範例 1️⃣ 動態圖片與錯誤備援

<template>
  <!-- 當 imgSrc 為 null 時,顯示預設圖片 -->
  <img :src="imgSrc || defaultImg" @error="onError" alt="使用者頭像">
</template>

<script>
export default {
  data() {
    return {
      imgSrc: null,               // 可能是 API 回傳的網址
      defaultImg: '/assets/avatar.png'
    }
  },
  methods: {
    onError(event) {
      // 圖片載入失敗時改用預設圖
      event.target.src = this.defaultImg
    }
  }
}
</script>

說明:利用 :src 能根據 imgSrc 的真偽值自動切換,避免空值導致的 404。


範例 2️⃣ 條件式 classstyle 結合

<template>
  <button
    :class="buttonClass"
    :style="buttonStyle"
    :disabled="isSubmitting"
    @click="handleClick"
  >
    {{ isSubmitting ? '送出中…' : '送出' }}
  </button>
</template>

<script>
export default {
  data() {
    return {
      isSubmitting: false,
      primary: true,
      rounded: false
    }
  },
  computed: {
    buttonClass() {
      return {
        'btn-primary': this.primary,
        'btn-rounded': this.rounded,
        disabled: this.isSubmitting
      }
    },
    buttonStyle() {
      return {
        cursor: this.isSubmitting ? 'not-allowed' : 'pointer',
        opacity: this.isSubmitting ? 0.6 : 1
      }
    }
  },
  methods: {
    handleClick() {
      this.isSubmitting = true
      // 模擬非同步操作
      setTimeout(() => (this.isSubmitting = false), 2000)
    }
  }
}
</script>

<style scoped>
.btn-primary { background:#42b983; color:#fff; padding:0.5rem 1rem; }
.btn-rounded { border-radius:9999px; }
.disabled { pointer-events:none; }
</style>

重點classstyle 皆使用 物件語法,讓條件式維護變得直觀。


範例 3️⃣ 物件語法一次綁定多屬性(Spread)

<template>
  <!-- linkAttrs 包含 href、target、rel 三個屬性 -->
  <a v-bind="linkAttrs">前往官方文件</a>
</template>

<script>
export default {
  data() {
    return {
      linkAttrs: {
        href: 'https://v3.vuejs.org/guide/template-syntax.html',
        target: '_blank',
        rel: 'noopener noreferrer'
      }
    }
  }
}
</script>

說明v-bind="object" 相當於把物件的每個鍵值對展開成獨立的屬性,省去重複寫 : 的繁瑣。


範例 4️⃣ 動態屬性名稱(Dynamic Argument)

<template>
  <div v-for="(value, key) in ariaAttrs" :[key]="value">
    <!-- 產生多個 aria-* 屬性 -->
    {{ key }} = {{ value }}
  </div>
</template>

<script>
export default {
  data() {
    return {
      ariaAttrs: {
        'aria-label': '關閉',
        'aria-hidden': false,
        'aria-controls': 'modal1'
      }
    }
  }
}
</script>

情境:在可存取性(a11y)需求較高的專案中,常需要根據資料動態生成 aria- 屬性,這時動態參數非常方便。


範例 5️⃣ 子元件 Prop 的一次性傳入(Spread Props)

<!-- Parent.vue -->
<template>
  <UserCard v-bind="cardData" @click="onCardClick"></UserCard>
</template>

<script>
import UserCard from './UserCard.vue'

export default {
  components: { UserCard },
  data() {
    return {
      cardData: {
        name: '王小明',
        age: 28,
        avatar: '/assets/avatar.jpg',
        isOnline: true
      }
    }
  },
  methods: {
    onCardClick() {
      console.log('卡片被點擊')
    }
  }
}
</script>
<!-- UserCard.vue -->
<template>
  <div class="card" :class="{ online: isOnline }">
    <img :src="avatar" alt="使用者頭像">
    <h3>{{ name }}</h3>
    <p>年齡:{{ age }}</p>
  </div>
</template>

<script>
export default {
  props: {
    name: String,
    age: Number,
    avatar: String,
    isOnline: Boolean
  }
}
</script>

<style scoped>
.card { border:1px solid #ddd; padding:1rem; }
.online { border-color:#42b983; }
</style>

亮點:父層只需要一次 v-bind="cardData" 就能把所有 props 傳入子元件,保持資料結構一致且易於維護。


常見陷阱與最佳實踐

陷阱 為什麼會發生 解決方式或最佳實踐
屬性名稱大小寫不一致 HTML 屬性只能使用 kebab-case,Vue 會自動將 camelCase 轉成 kebab-case,但在自訂屬性上若寫錯會無法匹配。 永遠使用 kebab-case 在模板中,camelCase 只保留在 JavaScript 的 props 定義。
布林屬性寫成字串 <input disabled="false"> 仍會被視為 true 使用 v-bind 並回傳布林值::disabled="isDisabled"
綁定 class/style 時忘記使用物件/陣列 直接寫 :class="myClass",若 myClassnull 會產生空字串,可能導致樣式錯誤。 確保返回值為 字串、陣列或物件,或在 computed 中預先處理。
v-bind 物件中使用連字符忘加引號 { 'data-id': 123 } 正確,{ data-id: 123 } 會被解析為減法運算。 使用引號 包住任何非合法變數名的鍵。
過度在模板內寫函式 每次重新渲染都會重新建立函式,影響效能。 把邏輯搬到 computedmethodssetup 中,僅在模板裡呼叫。
綁定自訂屬性忘記加 : <my-comp custom-prop="value"> 會把字串 "value" 當成 literal,無法與資料同步。 必須寫成 :custom-prop="value"(或 v-bind:custom-prop="value")。
使用 v-bind 物件時不小心把事件也帶入 v-bind="obj" 會把 obj 的每個鍵當屬性綁定,若裡面有 onClick 之類的鍵會變成 HTML 屬性而非事件。 只把屬性 放入綁定物件,事件仍使用 @click 等指令。

最佳實踐小結

  1. 預設使用縮寫 :,讓模板更簡潔。
  2. 盡量使用物件/陣列語法 來一次綁定多個屬性,減少重複程式碼。
  3. 布林屬性必須回傳布林值,避免字串誤判。
  4. 將條件或計算邏輯搬到 computed,保持模板的宣告式特性。
  5. 在子元件傳值時,優先使用 v-bind="object",保持資料結構一致,易於維護。
  6. 對於 classstyle,盡量使用陣列或物件語法,配合 computed 讓樣式變化更可控。

實際應用場景

場景 需求 v-bind 的角色
圖片懶載入 根據滾動位置切換 src,失敗時換成備援圖。 :src="computedSrc" + @error 處理。
表單驗證 欄位錯誤時自動加上 aria-invalidclass="error" :class="{ error: hasError }":aria-invalid="hasError"
動態主題切換 使用者可在暗色/亮色模式間切換,需改變 CSS 變數。 :style="{ '--primary-color': themeColor }"
可存取性(a11y) 依照 UI 狀態動態產生 aria-*role v-bind="ariaAttrs":[dynamicKey]="value"
多語系切換 文字內容與屬性(如 title)須即時更新。 :title="$t('tooltip.save')"{{ $t('button.submit') }}(結合 v-bind)。
動態組件 根據路由或使用者操作載入不同元件。 <component :is="currentComponent" v-bind="componentProps"></component>

實務提示:在大型專案中,建議將所有 屬性綁定的資料(例如 linkAttrsariaAttrs)集中管理於 storeComposable,這樣不僅能共享狀態,還能在單元測試時更容易 mock。


總結

v-bind 是 Vue 3 模板語法的核心之一,透過它我們可以:

  • 動態條件式批次 地將資料映射到 HTML 屬性或子元件的 props。
  • 使用 縮寫 :物件/陣列語法動態參數,讓程式碼更簡潔、可讀。
  • 正確處理 布林屬性class/style自訂屬性,避免常見的陷阱。
  • 實務場景(圖片懶載入、表單驗證、主題切換、可存取性、動態組件)中發揮關鍵作用。

掌握 v-bind 的細節,能讓你的 Vue 應用在 可維護性效能可讀性 上都有明顯提升。從今天起,試著把所有硬編碼的屬性改寫為動態綁定,感受 Vue 3 帶來的開發快感吧! 🎉