Vue3 教學:模板語法 – v-model 雙向綁定
簡介
在 Vue3 中,v-model 是最常使用的指令之一,負責在表單元件與資料狀態之間建立雙向綁定。透過 v-model,開發者可以省去手動監聽 input、change 事件與更新資料的繁瑣程式碼,讓 UI 與資料保持同步,提升開發效率與程式可讀性。
本篇將從概念、語法變化、實作範例,到常見陷阱與最佳實踐,完整說明 v-model 在 Vue3 的使用方式,幫助初學者快速上手,同時提供中階開發者進階的技巧與應用情境。
核心概念
1. 基本語法與雙向綁定原理
在 Vue2 中,v-model 只能用在 <input>、<textarea>、<select> 等原生表單元件;Vue3 則擴充支援 自訂元件,只要在子元件內使用 modelValue(預設屬性)與 update:modelValue 事件,即可完成雙向綁定。
<!-- 父層 -->
<input v-model="message" placeholder="輸入文字" />
<p>你輸入的是:{{ message }}</p>
// 父層的 script
export default {
data() {
return { message: '' }
}
}
- v-model 會自動把
message的值綁定到<input>的value屬性,並在使用者輸入時觸發input事件,將最新值回寫至message。
2. v-model 在自訂元件的使用
自訂元件若要支援 v-model,需要:
- props:接收
modelValue(或自訂名稱)。 - emits:在值變更時發出
update:modelValue(或自訂事件)。
<!-- 父層使用自訂元件 -->
<custom-input v-model="username" />
<p>使用者名稱:{{ username }}</p>
// CustomInput.vue
export default {
name: 'CustomInput',
props: {
modelValue: String // 接收外層傳入的值
},
emits: ['update:modelValue'],
template: `
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
`
}
重點:
v-model在自訂元件上等同於:modelValue="xxx" @update:modelValue="xxx = $event",因此只要遵守上述規則,即可在任意元件上使用雙向綁定。
3. 多個 v-model 變數與自訂參數
Vue3 允許在同一元件上使用 多個 v-model,只要在子元件中定義對應的 modelValue 替代名稱即可。
<!-- 父層 -->
<date-picker v-model:date="selectedDate" v-model:time="selectedTime" />
// DatePicker.vue
export default {
props: {
date: String,
time: String
},
emits: ['update:date', 'update:time'],
template: `
<div>
<input type="date"
:value="date"
@input="$emit('update:date', $event.target.value)" />
<input type="time"
:value="time"
@input="$emit('update:time', $event.target.value)" />
</div>
`
}
- 透過
v-model:propName的語法,開發者可以同時綁定多個資料欄位,提升元件的彈性。
4. 修飾符 (Modifiers) 的應用
v-model 內建三個常用修飾符:
| 修飾符 | 功能 | 範例 |
|---|---|---|
.lazy |
改為在 change 事件後才更新 |
<input v-model.lazy="msg"> |
.number |
轉換輸入值為 Number |
<input v-model.number="age"> |
.trim |
去除字串前後空白 | <input v-model.trim="name"> |
<input v-model.lazy.number.trim="price" placeholder="金額 (自動轉數字)" />
<p>目前金額:{{ price }}</p>
5. v-model 與 Composition API
在 Composition API 中,ref 與 reactive 都可以直接與 v-model 搭配使用。
<script setup>
import { ref } from 'vue'
const email = ref('')
</script>
<template>
<input v-model="email" placeholder="請輸入 Email" />
<p>已輸入:{{ email }}</p>
</template>
若要在自訂元件內使用 v-model,同樣以 props、emit 搭配 defineProps / defineEmits:
<script setup>
const props = defineProps({ modelValue: String })
const emit = defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="props.modelValue"
@input="emit('update:modelValue', $event.target.value)"
/>
</template>
程式碼範例
範例 1:基本文字輸入雙向綁定
<template>
<div>
<input v-model="msg" placeholder="說點什麼" />
<p>你說:{{ msg }}</p>
</div>
</template>
<script>
export default {
data() {
return { msg: '' }
}
}
</script>
說明:
msg會即時反映在<p>中,展示雙向同步的效果。
範例 2:使用 .number 與 .trim 修飾符
<template>
<div>
<input v-model.number.trim="quantity" placeholder="請輸入數量" />
<p>數量 (Number) = {{ quantity }},類型:{{ typeof quantity }}</p>
</div>
</template>
<script>
export default {
data() {
return { quantity: 0 }
}
}
</script>
說明:使用者即使輸入
" 12 ",最終會被轉成數字12,且不保留前後空白。
範例 3:自訂元件的雙向綁定 (單一 v-model)
<!-- Parent.vue -->
<template>
<custom-checkbox v-model="isChecked" />
<p>是否勾選:{{ isChecked }}</p>
</template>
<script>
import CustomCheckbox from './CustomCheckbox.vue'
export default {
components: { CustomCheckbox },
data() {
return { isChecked: false }
}
}
</script>
<!-- CustomCheckbox.vue -->
<template>
<label>
<input type="checkbox"
:checked="modelValue"
@change="$emit('update:modelValue', $event.target.checked)" />
勾選我
</label>
</template>
<script>
export default {
props: { modelValue: Boolean },
emits: ['update:modelValue']
}
</script>
說明:子元件透過
modelValue與update:modelValue完成雙向綁定,父層的isChecked會自動同步。
範例 4:多個 v-model(日期與時間)
<template>
<date-time-picker
v-model:date="selectedDate"
v-model:time="selectedTime"
/>
<p>日期:{{ selectedDate }},時間:{{ selectedTime }}</p>
</template>
<script>
import DateTimePicker from './DateTimePicker.vue'
export default {
components: { DateTimePicker },
data() {
return {
selectedDate: '',
selectedTime: ''
}
}
}
</script>
<!-- DateTimePicker.vue -->
<template>
<div>
<input type="date"
:value="date"
@input="$emit('update:date', $event.target.value)" />
<input type="time"
:value="time"
@input="$emit('update:time', $event.target.value)" />
</div>
</template>
<script>
export default {
props: { date: String, time: String },
emits: ['update:date', 'update:time']
}
</script>
說明:同一元件同時處理兩個綁定,適合需要同時取得多筆資料的表單 UI。
範例 5:v-model 與 Composition API 結合
<script setup>
import { ref } from 'vue'
const title = ref('Vue3 標題')
const content = ref('')
</script>
<template>
<input v-model="title" placeholder="標題" />
<textarea v-model="content" rows="4" placeholder="內容"></textarea>
<h2>{{ title }}</h2>
<p>{{ content }}</p>
</template>
說明:
ref直接作為 v-model 的資料來源,無需額外的data物件,程式碼更簡潔。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
未正確使用 modelValue |
在自訂元件忘記將 modelValue 作為 props,導致 v-model 無法更新。 |
確認子元件 props 名稱與 v-model 綁定的名稱一致,或使用 defineProps 指定。 |
| 事件名稱拼寫錯誤 | update:modelValue 拼寫錯誤會使雙向綁定失效。 |
使用常量或 TypeScript 型別 (defineEmits(['update:modelValue'])) 以減少拼寫錯誤。 |
使用 .lazy 時忘記同步 UI |
.lazy 只在 change 時更新,若 UI 需要即時回饋可能會產生不一致。 |
視需求選擇是否使用 .lazy,或自行在 @input 中同步資料。 |
| 綁定非原生可編輯元素 | 嘗試在 <div>、<span> 上使用 v-model,會因缺少 value/input 事件而失效。 |
只在可編輯的表單元件或自行實作 value 與 input 事件的自訂元件。 |
| 多個 v-model 的衝突 | 同時使用 v-model 與手動 :prop/@event 可能產生資料競爭。 |
統一 使用 v-model,或在手動綁定時確保不重複修改同一屬性。 |
最佳實踐
- 保持單一來源真相(Single Source of Truth):盡量讓資料只在父層管理,子元件僅負責展示與觸發事件。
- 使用 TypeScript:在大型專案中,透過型別檢查避免
modelValue/update:modelValue拼寫錯誤。 - 適度使用修飾符:
.number、.trim能減少手動轉型與清理的程式碼,但過度依賴會隱藏資料驗證需求,必要時自行在@input中做驗證。 - 避免過度雙向綁定:如果只需要單向資料流(例如只顯示),請改用
:prop,減少不必要的 reactivity 開銷。
實際應用場景
- 表單編輯器
- 使用 v-model 收集使用者輸入的文字、數字、日期等,配合表單驗證庫(如 VeeValidate)可快速完成 CRUD UI。
- 即時搜尋與過濾
- 透過
v-model.lazy或.debounce(自訂指令)把搜尋關鍵字綁定至 data,讓搜尋在使用者停止輸入後才觸發 API,提升效能。
- 透過
- 自訂 UI 元件庫
- 任何可重用的表單元件(如日期選擇器、下拉選單、開關)都應實作
modelValue/update:modelValue,讓使用者在組合頁面時只需使用v-model即可。
- 任何可重用的表單元件(如日期選擇器、下拉選單、開關)都應實作
- 多步驟導覽
- 在分頁式表單中,每一頁的輸入欄位皆透過 v-model 直接更新父層的狀態物件,最後一次性提交,避免在子頁面間傳遞大量 props。
- 即時協作編輯
- 結合 WebSocket,將 v-model 綁定的資料變更即時推送至其他使用者的介面,實現多人同時編輯的體驗。
總結
v-model 是 Vue3 中最直觀且功能強大的雙向綁定工具,從簡單的文字輸入到自訂元件的完整支援,都只需遵守 modelValue 與 update:modelValue 的約定。透過修飾符、v-model:propName 多綁定語法,以及與 Composition API 的自然結合,開發者可以在保持程式碼簡潔的同時,建立彈性高、可重用的表單元件。
在實務開發中,注意避免常見的綁定錯誤、適度使用修飾符、並遵循單一來源真相的原則,將使你的 Vue3 專案更易維護、效能更佳。希望本篇文章能幫助你快速掌握 v-model 的核心概念與最佳實踐,讓 UI 與資料同步變得輕而易舉。祝開發順利! 🚀