Vue : defineModel()
一、核心概念重述:组件中使用 v-model
的新方式
在 Vue 3.4 及以上版本中,推荐使用 defineModel()
宏来实现组件的双向数据绑定。它简化了以往通过 props
和 emit
手动实现 v-model
的繁琐过程。
✅ 示例回顾
<!-- Child.vue -->
<script setup>
const model = defineModel()
</script><template><input v-model="model" />
</template>
父组件:
<!-- Parent.vue -->
<Child v-model="countModel" />
子组件中的
model
是一个 ref,与父组件的countModel
实现双向同步。
二、底层机制详解
defineModel()
是一个编译时宏(compile-time macro),会被自动展开为传统的 prop + emit 模式。
🔧 编译前(Vue 3.4+ 推荐写法):
const model = defineModel()
⚙️ 编译后等效代码(Vue ❤️.4 写法):
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])// 模拟 ref 行为
const model = computed({get: () => props.modelValue,set: (value) => emit('update:modelValue', value)
})
🔄 父组件的 v-model
展开为:
<Child :modelValue="foo" @update:modelValue="val => foo = val"
/>
三、语法进阶:参数、默认值与修饰符
1. 带参数的 v-model
可以指定不同的模型名称,用于多个绑定:
<UserName v-model:first-name="first" v-model:last-name="last" />
子组件接收:
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
相当于监听
:firstName
和@update:firstName
。
2. 设置默认值和校验
// 必填
const model = defineModel({ required: true })// 默认值
const model = defineModel({ default: 0 })
⚠️ 警告:若设置了 default
但父组件未传值,则父子状态会不一致!
示例:
const model = defineModel({ default: 1 }) // 子组件为 1
const myRef = ref() // 父组件为 undefined
👉 此时 myRef
不会自动更新为 1
,造成不同步!
3. 自定义修饰符支持
defineModel()
支持解构获取修饰符信息:
const [model, modifiers] = defineModel()
console.log(modifiers) // 如 { capitalize: true }
结合 get
/ set
函数处理逻辑:
首字母大写 .capitalize
示例:
const [model, modifiers] = defineModel({set(value) {if (modifiers.capitalize) {return value.charAt(0).toUpperCase() + value.slice(1)}return value}
})
模板中使用:
<MyComponent v-model.capitalize="myText" />
4. 多个带修饰符的 v-model
<UserNamev-model:first-name.capitalize="first"v-model:last-name.uppercase="last"
/>
子组件分别获取修饰符:
const [firstName, firstNameModifiers] = defineModel('firstName')
const [lastName, lastNameModifiers] = defineModel('lastName')console.log(firstNameModifiers) // { capitalize: true }
console.log(lastNameModifiers) // { uppercase: true }
四、知识点总结
知识点一:defineModel()
宏的作用
是一个编译宏,返回 ref,实现父子组件间双向绑定,替代手动声明 props
与 emit
。
知识点二:v-model
参数机制
允许在组件上绑定多个 v-model
,每个对应不同字段名(如 v-model:title
→ title
),提升灵活性。
知识点三:修饰符处理(.trim
, .number
, 自定义)
通过 defineModel
返回的 modifiers
对象识别修饰符,配合 set
函数控制输入转换行为。
五、图表辅助理解
图表 1:defineModel()
编译原理流程图
图表 2:单个 v-model
vs 多个 v-model
对比
特性 | 单个 v-model | 多个 v-model |
---|---|---|
语法 | v-model="x" | v-model:field="x" |
子组件接收 | defineModel() | defineModel('field') |
底层 prop 名 | modelValue | field |
事件名 | update:modelValue | update:field |
使用场景 | 基础输入封装 | 表单拆分字段绑定 |
图表 3:修饰符工作流程
六、最佳实践建议
✅ 推荐做法:
Vue 3.4+ 优先使用
defineModel()
提高开发效率。多个字段绑定使用带参数的
v-model
。自定义修饰符时利用
set()
函数做值预处理。
🚫 避免陷阱:
不要轻易给
defineModel()
设default
,除非父组件明确传值或初始化。注意类型安全,必要时配合类型注解(TS 用户)。
七、结语总结
defineModel()
是 Vue 3.4 引入的重要语法糖,极大简化了组件双向绑定的实现方式。其背后仍基于 prop + emit
的响应式通信机制,但通过宏的方式隐藏了样板代码,使开发者能更专注于业务逻辑。
掌握其:
基本用法,
参数传递,
修饰符处理,
是构建高复用、易维护表单组件的关键技能。结合图表理解和实际演练,可快速上手并应用于复杂项目中。