一、核心用途
defineModel()是Vue 3.4+引入的编译器宏,用于简化组件间的双向数据绑定实现。它通过自动生成props和emit逻辑,替代传统的手动定义props和emit('update:xxx')的模式,使代码更简洁且类型安全。
主要优势:
- 代码精简:减少约40%的样板代码(如表单组件场景)。
- 类型推导:天然支持TypeScript类型声明。
- 多模型支持:可同时管理多个
v-model绑定。
二、基础与进阶用法示例
1. 基础绑定(默认modelValue)
<!-- 父组件 -->
<ChildComponent v-model="count" />
<!-- 子组件 -->
<script setup>
const model = defineModel() // 自动绑定父组件的v-model
</script>
<template>
<input v-model="model" />
</template>
等价于传统写法:
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="props.modelValue"
@input="emit('update:modelValue', $event.target.value)"
/>
</template>
2. 自定义属性名
<!-- 父组件 -->
<UserForm v-model:username="name" />
<!-- 子组件 -->
<script setup>
const username = defineModel('username', { default: 'Guest' })
</script>
3. 类型与修饰符
// 指定类型和默认值
const count = defineModel<number>('count', { default: 0 })
// 使用修饰符(如.trim)
const text = defineModel('text', {
set(value: string) { return value.trim() }
})
三、典型应用场景
-
表单控件
- 输入框、选择框等需要实时同步数据的场景16。
- 示例:封装一个带校验的输入框,父组件通过
v-model直接获取校验后的值。
-
状态切换组件
- 开关(Switch)、弹窗(Modal)等需要双向控制显隐状态的组件。
-
复杂组件通信
- 表格分页器(
v-model:page、v-model:pageSize)。 - 多选项卡(Tabs)组件同步当前激活的标签页。
- 表格分页器(
四、项目实践建议
- 类型安全:始终为
defineModel()声明TypeScript类型,避免any。 - 修饰符组合:通过
setter实现链式修饰符(如.trim.lazy)。 - 性能优化:对高频更新的数据(如拖拽组件),结合
debounce修饰符使用。
五、与传统模式对比
| 特性 | defineModel() | 传统props+emit |
|---|---|---|
| 代码量 | 1行声明 | 需定义props和emit |
| 类型支持 | 内置推导 | 需手动标注类型 |
| 多模型支持 | 直接声明多个defineModel | 需为每个模型重复定义 |
| 修饰符处理 | 通过set函数灵活实现 | 需手动处理事件参数 |