Vue3.0组件之间通信(defineProps 和 defineEmits 及 defineExpose)
前言:
- 一、父传子 defineProps
- 二、子传父 defineEmits
- 三、子组件暴露属性和方法给父组件 defineExpose
- 四、依赖注入Provide / Inject
在 <script setup>
中必须使用 defineProps
和 defineEmits
API 来声明 props
和 emits
,它们具备完整的类型推断并且在 <script setup>
中是直接可用的。
<template> <!-- 在模板中直接使用 props 中声明的变量 --> <h1>{{ msg }}</h1> <div>{{ title }}</div> </template> <script setup> const props = defineProps({ msg: String, title: { type: String, default: '我是标题' }, list: { type: Array, default: () => [] } }); console.log(props.msg); //使用props中的属性 const emits = defineEmits(['change', 'delete']); </script>
1. defineProps 和 defineEmits 、defineExpose 都是只能在 <script setup> 中才能使用,他们不需要被导入即可使用,并且会在编译 <script setup> 语法块时一同被编译。
2. defineProps 接收与 props 选项相同的值,defineEmits 也接收 emits 选项相同的。
defineProps 是Vue3的写法并且是一个仅在 <script setup> 中可使用的编译命令,并不需要显式地导入;在Vue3的非语法糖setup和在Vue2中的写法是 props 。
注意:defineProps() 中的参数不可以访问 <script setup> 中定义的其他变量,因为在编译时整个表达式都会被移到外部的函数中。
defineProps 只能在 setup 中使用,且只能在 setup 的顶层使用,不能在局部作用域使用。
和 vue2 一样,defineProps 里的属性只读,不能修改。
在子组件中可以使用defineProps声明需要接收父组件的哪些props,它需要定义一个包含props字段的对象,每个字段定义默认值和类型等信息。当父组件的props发生变化时,子组件也会随之响应。
defineProps支持的主要类型有:
- String
- Number
- Boolean
- Object
- Array
- Function
同时也支持许多高级类型,比如,枚举类型,对象类型,联合类型等等。
我们可以对props进行验证,确保传入的值符合我们期望的值。
- type:定义数据的类型
- required:是否必须
- default:默认值
- validator:自定义验证
const props = defineProps({ message: { type: String, default: '' }, count: { type: Number, required: true, default: 0, validator: (value) => { return value >= 0 && value <= 10 } } type:{ type: String, validator: (value) => { return ['success', 'warning', 'danger', 'info'].includes(value) } }, data:{ type: [Array, Object], default: () => { return { name: 'jack', age: 20 } } } })
实例:父组件传值给子组件
1、定义子组件
<template> 我是子组件 <p>子组件得到的name:{{ props.name }}</p> <p>子组件得到的age:{{ props.age }}</p> <!--可以省略前面的props直接写属性名---> <p>子组件得到的name:{{ name }}</p> <p>子组件得到的age:{{ age }}</p> </template> <script setup> //方式1: 以对象的形式去接收 const props = defineProps({ name: { type: String, default: "张三", }, age: { type: Number, default: 22 } }); // props.age = 18 // 报错,注意 defineProps 中的属性只读不能修改 //方式2: 以数组的方式去接收 //const childProps = defineProps(['name', 'age']); </script>
2、定义父组件
<template> name: {{ name }} <br /> age: {{ age }} <br /> <Child :name="name" :age="age" /> </template> <script setup> import { ref } from 'vue' import Child from './components/Child.vue' const name = ref('LiuQing'); const age = ref(18); </script>
defineEmits 和 defineProps 一样也是Vue3的写法并且仅用于 <script setup> 中,并且不需要导入;在Vue3的非语法糖setup和在Vue2中的写法是 emits 。
defineEmits 的不同点在于,组件要触发的事件可以显式地通过 defineEmits() 来声明。
defineEmits 用于子组件向父组件传递消息,在父组件中,只需要监听子组件的自定义事件,然后执行相应的逻辑即可。
注意:如果一个原生事件的名字 (例如 click) 被定义在 emits 选项中,则监听器只会监听组件触发的 click 事件而不会再响应原生的 click 事件。
实例:子组件向父组件传值
1、定义子组件
// 子组件 child.vue <template> <button @click="handelClick">传递给父级</button> <button @click="add">加</button> <button @click="decrease">减</button> </template> <script setup> const emits = defineEmits(['clickFn', 'add', 'decrease'])// 定义一个或多个自定义事件
// 触发emits事件 const handelClick = () => { emits('clickFn', { name: '张三', age: 18, id: 1 }) // 第一个参数为自定义事件名 第二个参数为要传递的数据 } const add = () => { emits('add', 10) // 第一个参数为自定义事件名 第二个参数为要传递的数据 } const decrease = () => { emits('decrease', 3) // 第一个参数为自定义事件名 第二个参数为要传递的数据 } </script>
2、定义父组件
// 父组件 parent.vue <template> <h3>年龄:{{ age }}</h3> <child :name="name" @clickFn="updateInfo" /> </template> <script setup> import { ref } from 'vue' import child from './components/child.vue' const name = ref('李四'); const age = ref(10); const updateInfo = (obj) => { console.log(obj) // { name: '张三', age: 18, id: 1 } name.value = obj.name; age.value = obj.age; } </script>
defineExpose 是Vue3中的一个新API,它允许子组件暴露其内部属性或方法给父组件访问。可以通过将属性或方法添加到defineExpose
函数中来实现。
获取用setup语法糖创建的子组件实例时,获取的实例是没有子组件自定义的属性和方法的,此时我们需要通过defineExpose
来暴露子组件的属性和方法。
在父组件中,我们使用ref
属性引用了子组件。需要注意的是,defineExpose
函数必须在setup
函数中调用,否则会报错。
1、定义子组件
<template> <p>子组件test</p> </template> <script setup> import { ref } from 'vue' const msg = ref('Hello Vue3') const a = () => { console.log(1) } const handleChangeMsg = (v) => { msg.value = v } defineExpose({ msg, a, handleChangeMsg }) </script>
2、定义父组件
<template> <Test ref="testInstanceRef" /> <button @click="handleChangeMsg">handleChangeMsg</button> </template> <script setup> import { ref, onMounted } from "vue"; import Test from "./components/Test.vue"; const testInstanceRef = ref(); onMounted(() => { const testInstance = testInstanceRef.value; console.log(testInstance.$el) // p标签 console.log(testInstance.msg) // msg属性 console.log(testInstance.a) // a方法,如果不使用defineExpose暴露是拿不到的 }) const handleChangeMsg = () => { testInstanceRef.value.handleChangeMsg('Hello TS') } </script>