前言在 Vue 3.4 中,defineModel 宏的引入标志着 Vue 双向绑定机制的一次重大革新。作为 Composition API 的重要补充,defineModel 不仅简化了代码结构,还显著提升了开发效率和代码可维护性。本文将深入探讨 defineModel 的核心原理、最佳实践以及在实际项目中的应用场景,展示其如何优雅地解决传统 v-model 实现中的痛点。传统双向绑定的痛点在 defineModel 出现之前,Vue 的双向绑定主要依赖于 v-model 和手动管理 props 和 emits。虽然这些方法有效,但在复杂场景下,代码往往显得冗长且难以维护。方案一:手动管理 props 和 emits父组件传递数据的同时需要实现一个修改数据的方法传递给子组件<!-- 父组件 -->
<child :carObj="carObj" @carPriceAdd="carPriceAdd" />
<script setup lang="ts">
const carObj = ref<ICarObj>({
brand: 'BMW',
price: 100000
})
const carPriceAdd = () => {
carObj.value.price += 1000
}
</script>子组件接收数据的同时还需要接收父组件传递过来的事件,并通过emits触发调用,就可以修改父组件的数据了<script setup lang="ts">
const props = defineProps<{
modelValue: IUser, // v-model
carObj: ICarObj // v-bind
}>()
const emits = defineEmits(['carPriceAdd'])
const priceAdd = () => {
emits('carPriceAdd')
console.log(props.carObj.price)
}
</script>方案二:使用 v-model还可以借助v-model,可以省去父组件定义修改数据的方法并传递给子组件这一步父组件通过v-model传递数据给子组件<child v-model="user" />
<script setup lang="ts">
const user = ref<IUser>({
name: 'song',
age: 18
})
</script>子组件在接受数据的同时也还要接受事件,只不过这个事件并不是父组件显式传递过来的,并且格式有点区别<script setup lang="ts">
const props = defineProps<{
modelValue: IUser, // v-model
carObj: ICarObj // v-bind
}>()
const emits = defineEmits(['update:modelValue'])
const ageAdd = () => {
emits('update:modelValue', {
...props.modelValue,
age: props.modelValue.age + 1
})
// console.log(props.modelValue.age)
}
</script>v-model默认传递过来的参数名为:modelValue,默认传递过来的事件为:update:modelValue默认参数名在父组件中可以修改,格式为:v-model:name,同理子组件中接受的数据名与事件名改成一致即可尽管 v-model 简化了部分代码,但仍需手动管理 props 和 emits,尤其是在处理多个双向绑定时,代码复杂度显著增加。所以从 Vue 3.4 开始,官方更加推荐使用 defineModel() 宏来实现双向数据绑定。defineModel 的诞生:简化双向绑定defineModel 是一个编译器宏,用于在 Vue 组件中定义双向绑定的 prop。它本质上是对 v-model 指令的语法糖,但提供了更简洁、更直观的语法。基本用法父组件还是不变,只需通过v-model传递数据给子组件即可<child v-model="user" />
<script setup lang="ts">
const user = ref<IUser>({
name: 'song',
age: 18
})
</script>通过 defineModel,子组件无需再显式接收 props 和 emits,直接通过 defineModel 返回的 ref 对象即可实现双向绑定。<script setup lang="ts">
// 通过defineModel声明父组件传递过来的数据,返回一个ref对象
const user = defineModel<IUser>('user', {
default: {}
})
// 子组件可以直接修改刚刚通过defineModel声明的数据,不需要通过emits,父组件会自动更新
const ageAdd = () => {
user.value.age += 1
}
</script>修饰符与转换器在一些特殊场景下,我们可能还需要使用v-model的修饰符功能比如:清除字符串末尾的空格父组件添加修饰符<!-- 父组件 -->
<child v-model:userName.trim="userName" />子组件获取修饰符在子组件中,我们可以通过解构 defineModel() 的返回值,来获取父组件添加到子组件 v-model 的修饰符:// 通过defineModel声明父组件传递过来的数据,返回一个ref对象
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。