一、defineProps() 和 defineEmits()
组件之间通讯,通过 props 和 emits 进行通讯,是单向数据流,
子组件不能改变父组件传递给它的 prop 属性,推荐的做法是它抛出事件,通知父组件自行改变绑定的值。
为了在声明 props 和 emits 选项时获得完整的类型推导支持,我们可以使用 defineProps 和 defineEmits API,它们将自动地在 <script setup> 中可用:
- 父组件:
<template> <div> <ChildMy v-model:count="count" /> {{ count }} </div> </template> <script setup> import ChildMy from './child.vue' import { ref } from 'vue' const count = ref(1) </script>
- 子组件:
<template> <div> {{ props.count }} <button @click="updatedCount">child btn</button> </div> </template> <script setup> const props = defineProps(["count"]); const emit = defineEmits(["update:count"]); const updatedCount = () => { emit('update:count', props.count + 1) } </script>
- defineProps 和 defineEmits 都是只能在
二、defineModel() 的双向绑定
这个宏可以用来声明一个双向绑定 prop,通过父组件的 v-model 来使用。
在底层,这个宏声明了一个 model prop 和一个相应的值更新事件。如果第一个参数是一个字符串字面量,它将被用作 prop 名称;否则,prop 名称将默认为 “modelValue”。在这两种情况下,你都可以再传递一个额外的对象,它可以包含 prop 的选项和 model ref 的值转换选项。
defineModel() 的双向绑定是在编译之后,创建了一个model的ref变量以及一个modelValue的props,并且watch了props中的modelValue;当子组件中的modelValue更新时,会触发update:modelValue事件,当父组件接收到这个事件时候,同时更新父组件的变量。
2.1、基础示例
- 父组件:
<template> <div> <ChildMy v-model="message"/> {{ message }} </div> </template> <script setup> import ChildMy from './child.vue' import { ref } from 'vue' const message = ref('hello') </script>
- 子组件:
<template> <div> {{ message }} <button @click="updatedMsg">child btn</button> </div> </template> <script setup> const message = defineModel() const updatedMsg = () => { message.value = `world` } </script>
2.2、定义类型
- 子组件:
<template> <div> {{ message }} <button @click="updatedMsg">child btn</button> </div> </template> <script setup> const message = defineModel({ type: String }) const updatedMsg = () => { message.value = `world` } </script>
2.3、声明prop名称
- 父组件:
<template> <div> <ChildMy v-model:count="count"/> {{ count }} </div> </template> <script setup> import ChildMy from './child.vue' import { ref } from 'vue' const count = ref(1) </script>
- 子组件:
<template> <div> {{ count }} <button @click="updatedCount">child btn</button> </div> </template> <script setup> const count = defineModel("count") const updatedCount = () => { count.value ++ } </script>
2.4、其他声明
- 子组件:
<template> <div> {{ count }} <button @click="updatedCount">child btn</button> </div> </template> <script setup> const count = defineModel("count", { type: Number, default: 0 , required: true}) const updatedCount = () => { count.value ++ } </script>
2.5、绑定多个值
- 父组件:
<template> <div> <ChildMy v-model:count="count" v-model:person="person" /> {{ person }} - {{ count }} </div> </template> <script setup> import ChildMy from './components/child.vue' import { ref,reactive } from 'vue' const count = ref(1) const person = reactive ({ name: 'Lucy', age: 11 }) </script>
- 子组件:
<template> <div> {{ person }} - {{ count }} <button @click="updatedData">child btn</button> </div> </template> <script setup> const person = defineModel("person") const count = defineModel("count") const updatedData = () => { count.value ++ person.value.age = 22 person.value.name = "lilei" } </script>
2.6、修饰符和转换器
为了获取 v-model 指令使用的修饰符,我们可以像这样解构 defineModel() 的返回值:
const [modelValue, modelModifiers] = defineModel()
当存在修饰符时,我们可能需要在读取或将其同步回父组件时对其值进行转换。我们可以通过使用 get 和 set 转换器选项来实现这一点:
- 父组件:
<template> <div> <ChildMy v-model.trim="message"/> {{ message }} </div> </template> <script setup> import ChildMy from './child.vue' import { ref } from 'vue' const message = ref(' hello ') </script>
- 子组件:
<template> <div> {{ message }} <button @click="updatedMsg">child btn</button> </div> </template> <script setup> const [message, modelModifiers] = defineModel({ set(value) { if (modelModifiers.trim) { value=value?.trim() } return value } }) const updatedMsg = () => { message.value += ` world` } </script>
2.7、修饰符串联
- 父组件:
<template> <div> <ChildMy v-model.trim.lowercase="message"/> {{ message }} </div> </template> <script setup> import ChildMy from './child.vue' import { ref } from 'vue' const message = ref('Hello') </script>
- 子组件:
<template> <div> {{ message }} <button @click="updatedMsg">child btn</button> </div> </template> <script setup> const [message, modelModifiers] = defineModel({ get(value) { if (modelModifiers.trim) { value=value?.trim() } if (modelModifiers.lowercase) { value=value?.toLowerCase(); } return value }, set(value) { if (modelModifiers.trim) { value=value?.trim() } if (modelModifiers.lowercase) { value=value?.toLowerCase(); } return value } }) const updatedMsg = () => { message.value += `World` } </script>