Vue 3 的 v-model 是可扩展的双向通信协议,核心是显式约定(modelValue/update:modelValue)、分层解耦(参数化 v-model:xxx)和按需增强(defineModel + modelModifiers)。

Vue 3 的 v-model 不再是“绑定 input 就行”的简单语法糖,而是一套可扩展、可组合、语义清晰的双向通信协议。写表单时若还沿用 Vue 2 思维或只写 v-model="xxx",容易在复杂场景下失控——比如多字段联动、修饰符定制、组件复用、校验嵌入等。符合现代规范的方案,核心在于三点:**显式约定、分层解耦、按需增强**。
明确 v-model 的底层契约:modelValue + update:modelValue
所有自定义表单组件必须遵守这个基础规则,这是整个方案的地基:
- 接收
modelValue作为默认 prop(不能直接修改它) - 内部值变更时,必须通过
emit('update:modelValue', newValue)向上通知 - 父组件无需额外声明
emits: ['update:modelValue'](Vue 3.4+ 自动识别,旧版建议显式声明)
例如封装一个带清空按钮的输入框,子组件内部逻辑应为:
const props = defineProps(['modelValue'])<br>const emit = defineEmits(['update:modelValue'])<br>const handleInput = (e) => emit('update:modelValue', e.target.value)<br>const clear = () => emit('update:modelValue', '')
用参数化 v-model 拆分关注点,避免“大对象传参”
当一个组件需要同步多个状态(如地址选择器要同时管理省、市、区),不要把它们塞进一个对象再传,而是用 v-model:xxx 显式声明每条数据通道:
Vue 3 组合式 API 与 最佳实践,涵盖代码组织、useXxx 模式、组合式函数设计、Store 集成、响应式优化及功能抽取。
立即学习“前端免费学习笔记(深入)”;
- 父组件写法:
<AddressSelect v-model:province="form.province" v-model:city="form.city" v-model:district="form.district" /> - 子组件对应接收
province、city、district三个 prop,并分别触发update:province、update:city等事件 - 好处是逻辑隔离、调试直观、支持独立校验和修饰符(如
v-model:phone.trim)
优先使用 defineModel(Vue 3.4+),减少样板代码
在支持 defineModel 的项目中,它应成为新建表单组件的默认起点。它把 props 声明、emits 声明、更新逻辑全部收束为一行:
const model = defineModel() // 默认绑定 modelValue<br>// 或带参数:<br>const name = defineModel('name')<br>const age = defineModel('age')
- 赋值即触发更新:
name.value = '张三'等价于emit('update:name', '张三') - 自动推导类型(TS 友好)、自动注册 emits、支持修饰符透传(如
.trim会出现在modelModifiers中) - 不推荐混用
defineModel和手动props + emit,保持统一
修饰符处理要落地到业务,不是堆内置能力
Vue 内置的 .trim、.number 只适用于原生 input;自定义组件想支持修饰符,需主动读取 modelModifiers 并响应:
- 在 setup 中解构获取:
const { modelModifiers } = defineProps(['modelValue']) - 判断是否启用:
if (modelModifiers.trim) newValue = newValue?.toString().trim() - 常见业务场景:手机号去空格、金额保留两位小数、搜索关键词自动转小写等,都可通过修饰符开关控制

















