本文 AI 產出,尚未審核

Vue3 教學:模板語法 – v-model 雙向綁定

簡介

在 Vue3 中,v-model 是最常使用的指令之一,負責在表單元件與資料狀態之間建立雙向綁定。透過 v-model,開發者可以省去手動監聽 inputchange 事件與更新資料的繁瑣程式碼,讓 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,需要:

  1. props:接收 modelValue(或自訂名稱)。
  2. 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 中,refreactive 都可以直接與 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,同樣以 propsemit 搭配 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>

說明:子元件透過 modelValueupdate: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 事件而失效。 只在可編輯的表單元件或自行實作 valueinput 事件的自訂元件。
多個 v-model 的衝突 同時使用 v-model 與手動 :prop/@event 可能產生資料競爭。 統一 使用 v-model,或在手動綁定時確保不重複修改同一屬性。

最佳實踐

  1. 保持單一來源真相(Single Source of Truth):盡量讓資料只在父層管理,子元件僅負責展示與觸發事件。
  2. 使用 TypeScript:在大型專案中,透過型別檢查避免 modelValue / update:modelValue 拼寫錯誤。
  3. 適度使用修飾符.number.trim 能減少手動轉型與清理的程式碼,但過度依賴會隱藏資料驗證需求,必要時自行在 @input 中做驗證。
  4. 避免過度雙向綁定:如果只需要單向資料流(例如只顯示),請改用 :prop,減少不必要的 reactivity 開銷。

實際應用場景

  1. 表單編輯器
    • 使用 v-model 收集使用者輸入的文字、數字、日期等,配合表單驗證庫(如 VeeValidate)可快速完成 CRUD UI。
  2. 即時搜尋與過濾
    • 透過 v-model.lazy.debounce(自訂指令)把搜尋關鍵字綁定至 data,讓搜尋在使用者停止輸入後才觸發 API,提升效能。
  3. 自訂 UI 元件庫
    • 任何可重用的表單元件(如日期選擇器、下拉選單、開關)都應實作 modelValue/update:modelValue,讓使用者在組合頁面時只需使用 v-model 即可。
  4. 多步驟導覽
    • 在分頁式表單中,每一頁的輸入欄位皆透過 v-model 直接更新父層的狀態物件,最後一次性提交,避免在子頁面間傳遞大量 props。
  5. 即時協作編輯
    • 結合 WebSocket,將 v-model 綁定的資料變更即時推送至其他使用者的介面,實現多人同時編輯的體驗。

總結

v-model 是 Vue3 中最直觀且功能強大的雙向綁定工具,從簡單的文字輸入到自訂元件的完整支援,都只需遵守 modelValueupdate:modelValue 的約定。透過修飾符、v-model:propName 多綁定語法,以及與 Composition API 的自然結合,開發者可以在保持程式碼簡潔的同時,建立彈性高、可重用的表單元件。
在實務開發中,注意避免常見的綁定錯誤、適度使用修飾符、並遵循單一來源真相的原則,將使你的 Vue3 專案更易維護、效能更佳。希望本篇文章能幫助你快速掌握 v-model 的核心概念與最佳實踐,讓 UI 與資料同步變得輕而易舉。祝開發順利! 🚀