➤ 簡單來說是讓「資料」和「畫面」雙向同步的一個語法
v-model
是 Vue 中用來實現 雙向綁定 的指令,讓資料變動會更新畫面、畫面互動也會同步更新資料,不用手動寫 :value="xxx"
和 @input="xxx = $event"
,使用者互動時,綁定的資料自動更新,省去手動取值的麻煩。背後實際上是一個 :value
搭配 @input
(或對應事件)的組合,封裝起來讓開發者更直覺地處理表單元素或元件資料。
- 文本類型的
<input>
和 <textarea>
元素會綁定 value
屬性並監聽 input
事件 <input type="checkbox">
和 <input type="radio">
會綁定 checked
屬性並監聽 change
事件<select>
會綁定 value
屬性並監聽 change
事件
v-model
可以在元件上使用以實現雙向綁定。而從 Vue 3.4 開始,推薦的實現方式是使用 defineModel()
宏(Macro),defineModel()
返回的值是一個 ref。它可以像其他 ref 一樣被訪問以及修改,且起到和父元件之間的雙向綁定作用:
<!-- Child.vue -->
<script setup>
// defineModel 由於是 Vue SFC 的編譯器宏(compiler macro),所以不需顯式引入
const model = defineModel()
</script>
<template>
<div>Parent bound v-model is: {{ model }}</div>
</template>
defineModel
是一個便利宏,編譯器將其展開為以下內容:
- 一個名為
modelValue
的 prop,本地 ref 的值與其同步 - 一個名為
update:modelValue
的事件,當本地 ref 的值發生變更時觸發
注意
如果在子元件的 defineModel()
設了 default
,請確保 父元件也主動給初始值,否則會出現「一開始不同步」的資料狀態。
// Parent.vue
// 沒有設定初始值
const myRef = ref()
<Child v-model="myRef" />
// Child.vue
const model = defineModel<number>({ default: 1 })
當一個元件內有多個欄位需要綁定(例如表單元件):
parent.vue
MyForm.vue
Preview
<script setup>
import { ref } from 'vue'
const form = ref({ name: 'Bob', age: 18 })
</script>
<template>
<MyForm
v-model:name="form.name"
v-model:age="form.age"
/>
</template>
<script setup lang="ts">
const props = defineProps<{
name: string;
age: number;
useDefineModel?: boolean;
}>();
const emit = defineEmits<{
(e: "update:name", value: string): void;
(e: "update:age", value: number): void;
}>();
// defineModel 版本(會在 useDefineModel 為 true 時使用)
const modelName = defineModel<string>("name");
const modelAge = defineModel<number>("age");
</script>
<template>
<div class="p-4 border rounded space-y-4">
<p>is useDefineModel: {{ props.useDefineModel }}</p>
<label class="block">
姓名:
<input
v-if="!props.useDefineModel"
type="text"
class="border px-2 py-1"
:value="props.name"
@input="emit('update:name', ($event.target as HTMLInputElement).value)"
/>
<input v-else v-model="modelName" type="text" class="border px-2 py-1" />
</label>
<label class="block">
年齡:
<input
v-if="!props.useDefineModel"
type="number"
class="border px-2 py-1"
:value="props.age"
@input="
emit('update:age', Number(($event.target as HTMLInputElement).value))
"
/>
<input v-else v-model="modelAge" type="number" class="border px-2 py-1" />
</label>
</div>
</template>
使用一般 v-model
Bob 的年齡是 18 歲
使用 definedModel
John 的年齡是 28 歲