v-model 的实现原理?
v-model 的本质是 语法糖。它背后并不是什么神奇的魔法,而是 Vue 编译器在编译阶段将其转换成了 属性绑定(Props) 和 事件监听(Events) 的组合。
简单来说,v-model 实现双向绑定的核心原理遵循 “数据向下流动(Props),事件向上冒泡(Events)” 的单向数据流原则。
我们可以分 原生 DOM 元素 和 自定义组件 两种情况来深入理解,同时也要注意 Vue 2 和 Vue 3 的区别。
1. 作用在原生 DOM 元素上 (如 <input>)
当你在 HTML 标签上使用 v-model 时,Vue 编译器会根据标签的类型自动展开为不同的属性和事件监听。
基本原理(以文本框为例)
<!-- 写法 -->
<input v-model="searchText" />
<!-- 编译后的等价代码 (原理) -->
<input
:value="searchText"
@input="searchText = $event.target.value"
/>
- Bind (绑定): 将数据
searchText绑定到 input 的value属性上。 - On (监听): 监听 input 的
input事件,当用户输入时,获取$event.target.value并重新赋值给searchText。
不同元素的差异
Vue 内部会根据元素类型智能处理:
- text / textarea: 使用
value属性 和input事件。 - checkbox / radio: 使用
checked属性 和change事件。 - select: 使用
value属性 和change事件。
特殊处理:中文输入法 (IME)
在输入中文(拼音)、日文等需要组合输入的语言时,原生的 input 事件会在你每敲击一次键盘时触发(比如输入 "nihao" 还没选词时)。
Vue 对此做了特殊处理:
- Vue 监听了
compositionstart(开始输入法输入) 和compositionend(结束输入法输入) 事件。 - 在
compositionstart触发后,Vue 会设置一个标志位,暂停v-model的数据更新。 - 直到
compositionend触发(即选词完成),才会触发数据的更新。
2. 作用在自定义组件上
在组件上使用 v-model 是实现父子组件通信的一种规范模式。这里 Vue 2 和 Vue 3 有明显的区别。
Vue 3 中的实现原理
Vue 3 默认使用 modelValue 作为 prop,update:modelValue 作为事件。
父组件使用:
<ChildComponent v-model="message" />
<!-- 编译后的等价代码 -->
<ChildComponent
:modelValue="message"
@update:modelValue="newValue => message = newValue"
/>
子组件实现:
// ChildComponent.vue
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
function onInput(e) {
// 触发事件,将新值传回父组件
emit('update:modelValue', e.target.value)
}
Vue 3 还支持多个 v-model (如 v-model:title,对应 prop title 和事件 update:title)。
Vue 2 中的实现原理
Vue 2 默认使用 value 作为 prop,input 作为事件。
父组件使用:
<ChildComponent v-model="message" />
<!-- 编译后的等价代码 -->
<ChildComponent
:value="message"
@input="newValue => message = newValue"
/>
Vue 2 的 model 选项:
如果子组件里 value 属性已经被占用了(比如 checkbox 组件),可以通过 model 选项修改默认的 prop 和 event 名:
export default {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
// ...
}
3. 总结
v-model 的实现原理一句话总结:
v-model是 Vue 提供的语法糖,在编译时会被转换为 属性绑定(Props,如value或modelValue)和 事件监听(Events,如input或update:modelValue)。它通过监听用户的输入事件来更新数据,并通过属性绑定将数据回显到视图上,从而实现双向绑定。
关键点速记:
- 语法糖:编译成
:prop+@event。 - 原生元素:根据 type 不同,绑定
value/checked和监听input/change。 - IME 问题:利用
compositionstart/end解决中文输入过程中的频繁更新问题。 - 组件差异:
- Vue 2:
:value+@input - Vue 3:
:modelValue+@update:modelValue
- Vue 2: