v-model可以实现父子组件之间的通信,能实现父子组件之间数据同步的业务。
1.原始的写法
FatherComponent.vue组件:
<script setup>
import {ref} from "vue"
import ChildComponent from "@/components/ChildComponent.vue"
//父组件的数据,钱数
const money = ref(1000)
//自定义事件的回调
const handler = (value) => {
console.log(value)
money.value = value
}
</script>
<template>
<div class="bg-blue h-75 w-100 ma-auto">
<h1 class="text-center">我是父组件</h1>
<h3>父组件的钱数:{{money}}块钱</h3>
<ChildComponent :modelValue="money" @update:modelValue="handler" />
</div>
</template>
这个部分可以这样写,这样就不需要定义回调函数了:
<ChildComponent :modelValue="money" @update:modelValue="$event=>(money=$event)" />
$event: 这是 Vue 中的事件对象。在这个特定的情况下,它是子组件通过emit触发的modelValue新值。
$event => (message = $event): 这是一个箭头函数,它将$event(即子组件传递的值)赋给父组件中的message变量。也可以理解为:(event) => { message = event; }
上面代码中的:<ChildComponent :modelValue="money" @update:modelValue="handler" />,就是我们熟悉props子传父和自定义事件子传父。
:modelValue="money":是props父传子,把父亲的数据money传到子组件。@update:modelValue="handler":自定义事件子传父,@update:modelValue是自定义事件(可能看着有点奇怪,但是必须就这么写!),handler是父组件内部的回调函数。
ChildComponent.vue组件:
<script setup>
//接受父组件给的数据
const props = defineProps(['modelValue'])
//接受父组件传递的自定义事件
const emit = defineEmits(['update:modelValue'])
//子组件内部的回调
const handler = () => {
//触发自定义事件,把modelValue + 100传递给父组件
emit('update:modelValue', props.modelValue + 100)
}
</script>
<template>
<div class="bg-purple h-50 w-75 ma-auto">
<h1 class="text-center">我是子组件</h1>
<h3>父组件给的钱数:{{modelValue}}块钱</h3>
<v-btn @click="handler" class="ml-6 bg-blue text-white">钱数+100</v-btn>
</div>
</template>
在子组件内部,通过下面的函数:
//子组件内部的回调
const handler = () => {
//触发自定义事件,把modelValue + 100传递给父组件
emit('update:modelValue', props.modelValue + 100)
}
也可以更简化的写,这样就不用再定义一个回调函数了:
<v-btn @click="$emit('update:modelValue', modelValue + 100)" class="ml-6 bg-blue text-white">钱数+100</v-btn>
触发父亲传递的自定义函数,并且修改父亲给子组件传递的modelValue的值,并传递给父组件。
父组件内部通过下面的回调函数:
//自定义事件的回调
const handler = (value) => {
console.log(value)
money.value = value
}
更改自己的money。
这样就可以父子组件之间的数据就是响应式的了。
2.简化写法
在子组件标签上写的这块儿代码,可以简化<ChildComponent :modelValue="money" @update:modelValue="handler" />
简化后: <ChildComponent v-model="money" />
下面是父组FatherComponent.vue件简化后的代码:
<script setup>
import {ref} from "vue"
import ChildComponent from "@/components/ChildComponent.vue"
//父组件的数据,钱数
const money = ref(1000)
</script>
<template>
<div class="bg-blue h-75 w-100 ma-auto">
<h1 class="text-center">我是父组件</h1>
<h3>父组件的钱数:{{money}}块钱</h3>
<ChildComponent v-model="money" />
</div>
</template>
ChildComponent.vu的代码不用动:
<script setup>
//接受父组件给的数据
const props = defineProps(['modelValue'])
//接受父组件传递自定义事件
const emit = defineEmits(['update:modelValue'])
//子组件内部的回调
const handler = () => {
//触发自定义事件,把modelValue + 100传递给父组件
emit('update:modelValue', props.modelValue + 100)
}
</script>
<template>
<div class="bg-purple h-50 w-75 ma-auto">
<h1 class="text-center">我是子组件</h1>
<h3>父组件给的钱数:{{modelValue}}块钱</h3>
<v-btn @click="handler" class="ml-6 bg-blue text-white">钱数+100</v-btn>
</div>
</template>
效果图:
3.使用最新的defineModel API
父组件:
<script setup>
import {ref} from "vue"
import ChildComponent from "./components/Child.vue"
//父组件的数据,钱数
const money = ref(1000)
</script>
<template>
<div class="bg-blue h-75 w-100 ma-auto">
<h1 class="text-center">我是父组件</h1>
<h3>父组件的钱数:{{money}}块钱</h3>
<ChildComponent v-model="money" />
</div>
</template>
子组件:
<script setup>
// 使用最新的defineModel API
const money = defineModel()
const changeMoney = () => {
money.value += 100
}
</script>
<template>
<div class="bg-purple h-50 w-75 ma-auto">
<h1 class="text-center">我是子组件</h1>
<h3>父组件给的钱数:{{modelValue}}块钱</h3>
<button @click="changeMoney" class="ml-6 bg-blue text-white">钱数+100</button>
</div>
</template>
这个新的api从vue3.4开始可以使用,我个人感觉这个api非常好用。