
Vue 中 v-model 的原理和自定义 v-model 怎么用一句话总结v-model 本质是「:valueinput」的语法糖自定义组件用modelValueupdate:modelValue即可实现双向绑定多个 v-model 用具名参数搞定正文目录v-model 到底做了什么5 大高频踩坑场景 修复代码万能兜底props emit computed 代理预防 checklist不再踩坑一句话总结一、v-model 到底做了什么原生表单元素上用 v-modeltemplate input v-modelmsg / /template等价于template input :valuemsg inputmsg $event.target.value / /template核心v-model 绑定 value 监听 input 事件。这不是魔法是语法糖。自定义组件上用 v-modelVue 3!-- 父组件 -- template MyInput v-modelmsg / /template等价于template MyInput :modelValuemsg update:modelValuemsg $event / /templateVue 3 的变化默认 prop 名从value改为modelValue事件名从input改为update:modelValue。二、5 大高频踩坑场景 修复代码① 自定义组件直接修改 modelValue!-- ❌ 子组件直接改 prop触发 Avoid mutating a prop 警告 -- script setup const props defineProps([modelValue]); const updateText (e) { props.modelValue e.target.value; // ❌ 直接改 prop }; /script template input :valuemodelValue inputupdateText / /template修复用 emit 通知父组件!-- ✅ 子组件用 emit 通知父组件修改 -- script setup const props defineProps([modelValue]); const emit defineEmits([update:modelValue]); const updateText (e) { emit(update:modelValue, e.target.value); // ✅ emit 通知 }; /script template input :valuemodelValue inputupdateText / /template② 用 v-model 绑定对象的属性父组件不更新!-- ❌ 子组件修改了对象属性但父组件的 v-model 没反应 -- script setup const props defineProps([modelValue]); const handleClick () { props.modelValue.name new name; // ❌ 直接改对象属性不触发 update }; /script修复emit 一个新对象!-- ✅ emit 新对象触发响应式更新 -- script setup const props defineProps([modelValue]); const emit defineEmits([update:modelValue]); const handleClick () { emit(update:modelValue, { ...props.modelValue, name: new name }); // ✅ 新对象 }; /script③ 一个组件需要多个 v-model!-- ❌ 只有一个 v-model其他属性只能用 prop emit 手动传 -- template UserForm v-modeluser :namename update:namename $event / /template修复Vue 3 支持多个具名 v-model!-- ✅ 多个 v-model 用具名参数 -- template UserForm v-model:namename v-model:ageage v-model:emailemail / /template!-- 子组件 -- script setup defineProps([name, age, email]); defineEmits([update:name, update:age, update:email]); /script④ v-model 绑定到computed但没写 setterscript setup import { computed } from vue; const props defineProps([modelValue]); const emit defineEmits([update:modelValue]); // ❌ 只读 computed 没有 setterv-model 写入时报错 const proxy computed(() { return props.modelValue.toUpperCase(); }); /script template input v-modelproxy / !-- ❌ 没有 setter无法写入 -- /template修复computed 写 get setscript setup import { computed } from vue; const props defineProps([modelValue]); const emit defineEmits([update:modelValue]); // ✅ computed 带 get/set代理 v-model const proxy computed({ get() { return props.modelValue; }, set(val) { emit(update:modelValue, val); } }); /script template input v-modelproxy / !-- ✅ 读写都通 -- /template⑤ 修饰符 .trim / .number / .lazy 在自定义组件上不生效!-- ❌ 父组件用了修饰符但子组件没处理 -- template MyInput v-model.trimmsg / !-- .trim 没有效果 -- /template修复子组件接收 modelModifiers 并处理!-- ✅ 子组件处理修饰符 -- script setup const props defineProps({ modelValue: String, modelModifiers: { default: () ({}) } // ✅ 接收修饰符对象 }); const emit defineEmits([update:modelValue]); const handleInput (e) { let val e.target.value; if (props.modelModifiers.trim) { val val.trim(); // ✅ 手动处理 .trim } emit(update:modelValue, val); }; /script template input :valuemodelValue inputhandleInput / /template三、万能兜底props emit computed 代理场景方案关键点基本双向绑定definePropsdefineEmitsprop 只读emit 通知需要转换值computed get/setget 读 propset emit多个双向绑定具名 v-modelv-model:xxx对象属性绑定emit 新对象{ ...old, key: newVal }修饰符处理modelModifiers手动 trim/number/lazy万能模板!-- 父组件 -- script setup import { ref } from vue; import CustomInput from ./CustomInput.vue; const msg ref(); const user ref({ name: , age: 0 }); /script template !-- 基本用法 -- CustomInput v-model.trimmsg / !-- 对象用法 -- CustomForm v-modeluser / /template!-- 子组件 CustomInput.vue -- script setup import { computed } from vue; const props defineProps({ modelValue: String, modelModifiers: { default: () ({}) } }); const emit defineEmits([update:modelValue]); // ✅ computed 代理处理修饰符 const proxy computed({ get() { return props.modelValue; }, set(val) { if (props.modelModifiers.trim) val val.trim(); emit(update:modelValue, val); } }); /script template input v-modelproxy / /template四、预防 checklist自定义组件 v-model永远不要直接改 modelValue prop用 emit需要转换值时用computed get/set代理别手动写 input 事件多个双向绑定用具名 v-modelv-model:xxx不要叠一堆 prop emit绑定对象属性时emit 新对象不要直接改原对象属性修饰符在自定义组件上需要手动处理子组件接收modelModifiersVue 2 和 Vue 3 的 v-modelprop 名不同value vs modelValue迁移注意表单组件封装时先写 computed 代理再绑 v-model一步到位五、一句话总结「v-model :valueinput的语法糖」自定义组件用modelValueupdate:modelValue实现双向绑定需要转换值就上 computed get/set多个绑定就用具名 v-model。记住prop 只读永远 emit最后问候亲爱的朋友们并邀请你们阅读我的全新著作 《Vue.js 3企业级项目开发实战微课视频版》