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 的布林屬性(如 disabled、readonly)在寫成字串時會失去布林意義。使用 v-bind 時,只要表達式回傳 true 或 false,Vue 會自動在真值時加入屬性、在偽值時移除屬性。
<button :disabled="isSubmitting">送出</button>
當
isSubmitting為true時,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)
對於 class 或 style 綁定,陣列語法可以把多個條件式或計算結果組合在一起。
<!-- class 會根據條件自動合併 -->
<div :class="[baseClass, isActive ? 'active' : '', extraClass]"></div>
6. 綁定 class、style 的特殊寫法
- 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️⃣ 條件式 class、style 結合
<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>
重點:
class、style皆使用 物件語法,讓條件式維護變得直觀。
範例 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",若 myClass 為 null 會產生空字串,可能導致樣式錯誤。 |
確保返回值為 字串、陣列或物件,或在 computed 中預先處理。 |
在 v-bind 物件中使用連字符忘加引號 |
{ 'data-id': 123 } 正確,{ data-id: 123 } 會被解析為減法運算。 |
使用引號 包住任何非合法變數名的鍵。 |
| 過度在模板內寫函式 | 每次重新渲染都會重新建立函式,影響效能。 | 把邏輯搬到 computed、methods 或 setup 中,僅在模板裡呼叫。 |
綁定自訂屬性忘記加 : |
<my-comp custom-prop="value"> 會把字串 "value" 當成 literal,無法與資料同步。 |
必須寫成 :custom-prop="value"(或 v-bind:custom-prop="value")。 |
使用 v-bind 物件時不小心把事件也帶入 |
v-bind="obj" 會把 obj 的每個鍵當屬性綁定,若裡面有 onClick 之類的鍵會變成 HTML 屬性而非事件。 |
只把屬性 放入綁定物件,事件仍使用 @click 等指令。 |
最佳實踐小結
- 預設使用縮寫
:,讓模板更簡潔。 - 盡量使用物件/陣列語法 來一次綁定多個屬性,減少重複程式碼。
- 布林屬性必須回傳布林值,避免字串誤判。
- 將條件或計算邏輯搬到
computed,保持模板的宣告式特性。 - 在子元件傳值時,優先使用
v-bind="object",保持資料結構一致,易於維護。 - 對於
class、style,盡量使用陣列或物件語法,配合computed讓樣式變化更可控。
實際應用場景
| 場景 | 需求 | v-bind 的角色 |
|---|---|---|
| 圖片懶載入 | 根據滾動位置切換 src,失敗時換成備援圖。 |
:src="computedSrc" + @error 處理。 |
| 表單驗證 | 欄位錯誤時自動加上 aria-invalid、class="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>。 |
實務提示:在大型專案中,建議將所有 屬性綁定的資料(例如
linkAttrs、ariaAttrs)集中管理於 store 或 Composable,這樣不僅能共享狀態,還能在單元測試時更容易 mock。
總結
v-bind 是 Vue 3 模板語法的核心之一,透過它我們可以:
- 動態、條件式、批次 地將資料映射到 HTML 屬性或子元件的 props。
- 使用 縮寫
:、物件/陣列語法、動態參數,讓程式碼更簡潔、可讀。 - 正確處理 布林屬性、class/style、自訂屬性,避免常見的陷阱。
- 在 實務場景(圖片懶載入、表單驗證、主題切換、可存取性、動態組件)中發揮關鍵作用。
掌握 v-bind 的細節,能讓你的 Vue 應用在 可維護性、效能、可讀性 上都有明顯提升。從今天起,試著把所有硬編碼的屬性改寫為動態綁定,感受 Vue 3 帶來的開發快感吧! 🎉