v-model 与表单组件绑定
约 628 字大约 2 分钟
布欧-Lewyon
2026-05-15
首页 › Vue › 组件与通信 › v-model 与表单组件绑定
v-model 是 Vue 中"双向绑定"的语法糖。理解它的底层机制,才能写出优雅的自定义组件。
v-model 的本质
原生元素上
<input v-model="text" />
<!-- 等价于 -->
<input :value="text" @input="text = $event.target.value" /><input type="checkbox" v-model="checked" />
<!-- 等价于 -->
<input type="checkbox" :checked="checked" @change="checked = $event.target.checked" />组件上
<MyInput v-model="value" />
<!-- 等价于 -->
<MyInput :modelValue="value" @update:modelValue="value = $event" />子组件中用 modelValue 接收,触发 update:modelValue 事件。
自定义 v-model 组件
<!-- MyInput.vue (Options API) -->
<script>
export default {
props: {
modelValue: { type: String, default: '' }
},
emits: ['update:modelValue'],
methods: {
onInput(e) {
this.$emit('update:modelValue', e.target.value)
}
}
}
</script>
<template>
<input :value="modelValue" @input="onInput" class="custom-input" />
</template>使用:
<MyInput v-model="searchText" />多个 v-model
Vue 3 允许一个组件绑定多个 v-model:
<UserForm
v-model:first-name="firstName"
v-model:last-name="lastName"
/>子组件:
<!-- UserForm.vue -->
<script>
export default {
props: {
firstName: String,
lastName: String
},
emits: ['update:firstName', 'update:lastName']
}
</script>
<template>
<input :value="firstName" @input="$emit('update:firstName', $event.target.value)" />
<input :value="lastName" @input="$emit('update:lastName', $event.target.value)" />
</template>v-model 修饰符
内置修饰符
<input v-model.trim="text" /> <!-- 去空格 -->
<input v-model.number="age" /> <!-- 转数字 -->
<input v-model.lazy="text" /> <!-- 惰性(change 事件) -->自定义修饰符
<!-- 父组件 -->
<MyInput v-model.capitalize="text" />子组件通过 modelModifiers 接收:
<script>
export default {
props: {
modelValue: String,
modelModifiers: { default: () => ({}) } // 接收修饰符
},
emits: ['update:modelValue'],
methods: {
onInput(e) {
let value = e.target.value
if (this.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
this.$emit('update:modelValue', value)
}
}
}
</script>defineModel(Vue 3.4+)
Vue 3.4 引入的编译宏——v-model 组件的更简洁写法,仅在 <script setup> 中可用:
<!-- MyInput.vue (Composition API) -->
<script setup>
const model = defineModel({
type: String,
default: ''
})
function onInput(e) {
model.value = e.target.value
}
</script>
<template>
<input :value="model" @input="onInput" />
</template>多个 defineModel:
<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>
<template>
<input v-model="firstName" />
<input v-model="lastName" />
</template>
defineModel是一个编译宏,无需 import,自动处理 props 声明 + emit 触发。
实战:评分组件
<!-- Rate.vue -->
<script>
export default {
props: {
modelValue: { type: Number, default: 0 },
count: { type: Number, default: 5 }
},
emits: ['update:modelValue'],
computed: {
stars() {
return Array.from({ length: this.count }, (_, i) => i + 1)
}
},
methods: {
select(val) {
this.$emit('update:modelValue', val)
}
}
}
</script>
<template>
<span class="rate">
<span
v-for="star in stars"
:key="star"
:class="{ active: star <= modelValue }"
@click="select(star)"
class="star"
>
★
</span>
</span>
</template>
<!-- 使用 -->
<Rate v-model="rating" :count="5" />小结
| 概念 | 要点 |
|---|---|
| v-model 本质 | :modelValue + @update:modelValue 语法糖 |
| 多个 v-model | v-model:xxx,子组件声明对应 props/emits |
| defineModel | Vue 3.4+ 编译宏,简化 v-model 组件编写 |
| 修饰符 | 内置(.trim/.number/.lazy)+ 自定义(modelModifiers) |
上一节:Slot 内容分发 下一节:provide / inject 与依赖注入
