第 32 题:Vue3 v-model 原理(语法糖 → props + emit → modelValue → update:modelValue)
回答结构:
核心回答 → 双向绑定机制 → 多个 v-model → 自定义组件实现 → 源码解析 → 面试官追问 → 高分总结
🎯 一、核心回答(面试官最关注)
Vue3 中 v-model 本质是:
props + emits 的语法糖
默认使用:
- prop:
modelValue- event:
update:modelValue
你写:
ini
<MyInput v-model="msg" />
Vue 会转换成:
ini
<MyInput
:modelValue="msg"
@update:modelValue="msg = $event"
/>
也就是说:
- 父组件通过
modelValue把值传给子组件 - 子组件通过
emit("update:modelValue", newValue)把值传回父组件
🎯 二、v-model 的完整数据流(非常重要)
vbnet
父组件数据(msg)
↓ 作为 prop 传递
modelValue
↓ 子组件修改后 emit
update:modelValue
↓
父组件接收 $event → 更新 msg
这就是 Vue3 的单向数据流 + 事件回传。
🎯 三、子组件必须如何写?
标准写法:
xml
<script setup>
const props = defineProps({
modelValue: String
})
const emit = defineEmits(["update:modelValue"])
function update(e) {
emit("update:modelValue", e.target.value)
}
</script>
<template>
<input :value="props.modelValue" @input="update" />
</template>
核心:emit 必须叫 update:modelValue
🎯 四、多个 v-model 原理(Vue3 强化的功能)
你可以写:
ini
<MyRange v-model:start="start" v-model:end="end" />
Vue 会转成:
ini
<MyRange
:start="start"
:end="end"
@update:start="start = $event"
@update:end="end = $event"
/>
子组件写法:
arduino
const props = defineProps(["start", "end"])
const emit = defineEmits(["update:start", "update:end"])
emit("update:start", newStart)
emit("update:end", newEnd)
Vue2 完全不支持多 v-model
这是 Vue3 的重大升级。
🎯 五、v-model 修饰符原理(.trim、.number、自定义修饰符)
例如:
ini
<MyInput v-model.trim="msg" />
会编译成:
ini
<MyInput
:modelValue="msg"
@update:modelValue="msg = $event.trim()"
/>
自定义修饰符:
ini
<MyInput v-model.capitalize="msg" />
Vue 会给 props 添加:
yaml
modelModifiers: { capitalize: true }
子组件可读取:
ini
if (props.modelModifiers.capitalize) {
value = value.toUpperCase()
}
🎯 六、源码级解析(重点是 compile 阶段)
Vue 在模板编译时会把:
ini
v-model="xxx"
转换成:
css
{
props: [
createObjectProperty("modelValue", xxx)
],
events: [
createObjectProperty("onUpdate:modelValue",
createAssignmentExpression(xxx, $event)
)
]
}
也就是说:
- 编译阶段完全变成 props + emit
- 运行时不需要任何特殊处理
🎯 七、面试官追问(附高分回答)
❓1:Vue3 为什么把 v-model 的字段改成 modelValue?
高分回答:
- 统一所有组件的输入 prop(没有 value/input 混乱)
- 多 v-model 必须有字段名
- modelValue 更标准化
Vue2:
valueinputcheckedchange
非常零散。
Vue3 彻底统一。
❓2:为什么 update:modelValue 这么设计?
因为:
- Vue3 的事件是通过字符串绑定
- 冒号语法能清晰表示"更新某个 prop"
- 可以支持多个 v-model
这是经典的可扩展性设计。
❓3:多个 v-model 怎么区分?
靠:
makefile
v-model:title
v-model:desc
v-model:start
v-model:end
字段名前缀。
编译后变成:
ruby
:modelValue → :title
@update:modelValue → @update:title
❓4:什么时候用 watch 代替 v-model?
回答:
- 当你需要监听变化但不希望自动更新值时
- 当你希望多个 props 组合成一个值时
否则用 v-model 更简洁。
🎯 八、金牌总结(背下来即可)
Vue3 的 v-model 是
modelValue + update:modelValue的语法糖,通过 props 单向流动传值,通过 emit 进行反向更新,实现了可控的双向绑定。
支持多个 v-model,自定义修饰符,编译阶段将 v-model 转换为标准化的 props + emit 结构,是 Vue3 数据流设计中的关键部分。