原理
通过 :value 和 @input 事件实现的
// 在书写方面这样写
<!-- Parent.vue -->
<MyComponent v-model:title="bookTitle" />
<!-- MyComponent.vue -->
<script setup>
defineProps({
title: {
required: true
}
})
defineEmits(['update:title'])
</script>
<template>
<input
:value="title"
@input="$emit('update:title', $event.target.value)"
/>
</template>
-----------------------------------
// 父组件中的v-model:title会被编译成
<!-- Parent.vue -->
<MyComponent
:title="bookTitle"
@update:title="$event => (bookTitle = $event)"
/>
如你所见,这显得冗长得多。然而,这样写有助于理解其底层机制。(摘自Vue官网)
绑定一个prop都如此冗长,如果绑定多个prop,可想而知.
解决方案
在2023-12-29的时候Vue发布了3.4正式版本,自3.4版本之后,官网推荐v-model在父子组件的双向绑定上推荐使用defineModel()宏
// 即使有多个prop要绑定
<!-- Parent.vue -->
<MyComponent v-model:title="bookTitle" v-model:content="bookContent" />
<!-- MyComponent.vue -->
<script setup>
const title = defineModel('title')
const content = defineModel('content')
</script>
<template>
<input type="text" v-model="title" />
<input type="text" v-model="content" />
</template>
这样的写法是不是很美观
需注意
defineModel支持一个默认值,但在使用的时候,如果子组件中defineModel中设置了默认值,但是父组件中在初始化prop的时候没有给值,这样会导致父子组件不同步的问题.
const foo = ref() // 未传值
<Child v-model="foo">
<!-- Child.vue -->
const model = defineModel({ default: 0 }) //却给了默认值
如上代码,父组件中foo值为undefined,子组件中为0
如果要自定义更新时间名称,还要传递默认值等配置信息怎么写
<!-- Parent.vue -->
<MyComponent v-model:title="bookTitle" />
<!-- Child.vue -->
<script setup>
const title = defineModel('title', {default: "title", required: true})
</script>
<template>
<input type="text" v-model="title" />
</template>