在 Vue 开发中,v-model
是我们最熟悉的"老朋友"。
vue
<input v-model="message" />
短短一行代码,实现了数据到视图、视图到数据的自动同步。
但你是否好奇:
"为什么输入框内容改变,
message
就自动更新?"
"自定义组件也能用v-model
吗?它是怎么工作的?"
本文将带你深入 v-model
的底层机制,揭开它作为 "语法糖" 的真实面目。
一、v-model 的本质:双向绑定的语法糖
✅ 核心定义
v-model
并不是真正的"双向绑定",而是一个 语法糖(Syntactic Sugar),它简化了:
- 数据 → 视图 :通过
v-bind
绑定值; - 视图 → 数据 :通过
v-on
监听事件并更新数据。
💡 它的本质是 "数据绑定 + 事件监听" 的组合。
二、场景一:作用在原生表单元素上
📌 基础用法
vue
<template>
<input v-model="message" />
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue'
};
}
};
</script>
🔁 等价于(手动实现)
vue
<input
:value="message"
@input="message = $event.target.value"
/>
📊 拆解原理
方向 | 实现方式 |
---|---|
数据 → 视图 | :value="message" 将 message 的值绑定到 <input> 的 value 属性 |
视图 → 数据 | @input="..." 监听输入事件,将 $event.target.value 赋值回 message |
💡
$event
是原生 DOM 事件对象,$event.target.value
是输入框的当前值。
📌 不同表单元素的 v-model 行为
元素类型 | 绑定属性 | 监听事件 |
---|---|---|
<input type="text"> |
value |
input |
<textarea> |
value |
input |
<input type="checkbox"> |
checked |
change |
<input type="radio"> |
checked |
change |
<select> |
value |
change |
💡 示例:复选框
vue
<input type="checkbox" v-model="isChecked" />
<!-- 等价于 -->
<input
:checked="isChecked"
@change="isChecked = $event.target.checked"
/>
三、场景二:作用在自定义组件上
✅ 核心机制
在组件上,v-model
是一个 父子组件通信的语法糖,基于:
props
:向下传递数据;$emit
:向上触发事件。
📌 默认行为
- prop 名称 :
value
- 事件名称 :
input
🔁 父组件写法
vue
<child-component v-model="message" />
🔁 等价于
vue
<child-component
:value="message"
@input="message = $event"
/>
📌 子组件实现
vue
<!-- ChildComponent.vue -->
<template>
<input
:value="value"
@input="$emit('input', $event.target.value)"
/>
</template>
<script>
export default {
props: ['value'] // 接收父组件传入的 value
};
</script>
🔄 数据流详解
- 父 → 子 :
message
→value
prop; - 子 → 父 :用户输入 → 触发
input
事件 →$emit('input', 新值)
→ 父组件更新message
。
🛠️ 自定义 prop 和 event 名称(Vue 2.2+)
使用 model
选项可以修改默认的 value
和 input
。
📌 场景:复选框组件想用 checked
作为 prop
vue
<!-- SwitchComponent.vue -->
<template>
<label>
<input
type="checkbox"
:checked="checked"
@change="$emit('change', $event.target.checked)"
/>
开关
</label>
</template>
<script>
export default {
model: {
prop: 'checked', // 使用 checked 作为 prop
event: 'change' // 使用 change 作为事件
},
props: ['checked']
};
</script>
vue
<!-- 父组件 -->
<switch-component v-model="isOn" />
<!-- 等价于 -->
<switch-component
:checked="isOn"
@change="isOn = $event"
/>
🧩 Vue 3 中的 v-model 变化
Vue 3 对 v-model
进行了升级:
- 不再默认使用
value
prop; - 支持多个
v-model
绑定; - 默认 prop 名为
modelValue
,事件名为update:modelValue
。
💡 Vue 3 写法
vue
<!-- 父组件 -->
<custom-input v-model="message" />
<!-- 等价于 -->
<custom-input
:modelValue="message"
@update:modelValue="message = $event"
/>
vue
<!-- 子组件 -->
<script>
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
methods: {
onInput(e) {
this.$emit('update:modelValue', e.target.value);
}
}
}
</script>
四、v-model 的修饰符
v-model
支持修饰符,进一步增强功能:
修饰符 | 作用 |
---|---|
.lazy |
从 input 事件改为 change 事件 |
.number |
自动将输入值转换为数字 |
.trim |
自动去除输入值首尾空格 |
💡 示例
vue
<!-- 失去焦点时才更新 -->
<input v-model.lazy="message" />
<!-- 输入自动转为数字 -->
<input v-model.number="age" type="text" />
<!-- 去除空格 -->
<input v-model.trim="username" />
五、常见误区与最佳实践
❌ 误区 1:v-model 是真正的双向绑定
✅ 正确认知:它是语法糖,基于单向数据流(props down, events up)。
❌ 误区 2:所有组件都适合用 v-model
✅ 建议:仅用于"输入型"组件(如输入框、选择器、开关等)。
✅ 最佳实践
- 明确数据流向 :始终记住
v-model
是:value + @input
的简写; - 合理使用
model
选项:避免 prop 名称冲突; - Vue 3 中使用
defineModel
(实验性):更简洁的双向绑定。
💡 结语
"v-model = v-bind + v-on"
场景 | 语法糖展开 |
---|---|
原生表单 | :value + @input |
自定义组件(Vue 2) | :value + @input |
自定义组件(Vue 3) | :modelValue + @update:modelValue |
掌握 v-model
的本质,你就能:
✅ 理解数据流动; ✅ 自定义复杂表单组件; ✅ 避免响应式陷阱。