在 Vue 模板中,一句简洁的 v-model="value"
就能完成双向绑定。它看似魔法,实则是一套约定优先的设计范式:把数据与用户输入事件封装成统一的接口。理解其底层转换规则,有助于我们在自定义组件、复杂表单场景中写出更可维护的代码。
一、v-model 的本质
无论作用于原生表单元素还是自定义组件,v-model
在编译阶段都会被展开为一对属性 + 事件的绑定。
展开规则由 Vue 的模板编译器根据节点类型与组件配置决定。
二、作用于原生表单
编译器会依据标签类型选择最合适的属性名与事件名:
input[type=text]
→value
+input
input[type=checkbox]
→checked
+change
select
→value
+change
示例代码
html
<input v-model="msg">
<!-- 编译后 -->
<input :value="msg" @input="msg = $event.target.value">
开发者无需记忆细节,Vue 在编译阶段完成映射,确保行为一致。
三、作用于自定义组件:约定优于配置
默认情况下,自定义组件的 v-model
展开为:
html
<Comp v-model="data" />
<!-- 编译后 -->
<Comp :value="data" @input="data = $event" />
组件内部只需实现 props.value
与 $emit('input', newVal)
即可。
若组件需要自定义属性名或事件名,可通过 model
选项覆盖:
js
export default {
model: {
prop: 'number',
event: 'change'
},
props: ['number'],
methods: {
update(val) {
this.$emit('change', val)
}
}
}
此时
html
<Comp v-model="data" />
<!-- 编译后 -->
<Comp :number="data" @change="data = $event" />
该机制使得自定义输入组件与原生表单保持同一心智模型。
四、设计启示:统一接口,隔离变化
v-model 的展开策略体现了面向接口编程思想:
- 对调用者:始终用
v-model
描述"值与事件"的绑定关系; - 对实现者:通过
model
选项自由切换内部实现,而无需修改父级代码。
这种约定降低了组件之间的耦合度,也方便了第三方组件库的接入。
总结
v-model是 Vue 在编译期自动生成的双向数据流约定。掌握其展开规则,不仅能在面试中清晰阐述原理,更能在实际开发中设计出高复用、低耦合的输入组件。