Vue 组件通信
一、 父子通信
1.defineProps
- 运行时声明:传递给
defineProps()
的参数会作为运行时的props
选项使用
vue
<!-- Parent.vue -->
<Child foo="Hello" bar="1" />
<!-- Child.vue -->
<script setup lang="ts">
const props = defineProps({
foo: { type: String, required: true },
bar: Number,
});
props.foo; // string
props.bar; // number | undefined
</script>
- 基于类型的声明:通过泛型参数来定义 props 的类型
vue
<script setup lang="ts">
const props = defineProps<{
foo: string;
bar?: number;
}>();
</script>
也可以将 props 的类型移入一个单独的接口中:
vue
<script setup lang="ts">
interface Props {
foo: string;
bar?: number;
}
const props = defineProps<Props>();
</script>
2.默认值 withDefaults
当使用基于类型的声明时,我们失去了为 props
声明默认值的能力。这可以通过 withDefaults
编译器宏解决:
js
export interface Props {
msg?: string
labels?: string[]
}
const props = withDefaults(defineProps<Props>(), {
msg: 'hello',
labels: () => ['one', 'two']
})
这将被编译为等效的运行时 props default
选项。此外,withDefaults
帮助程序为默认值提供类型检查,并确保返回的 props 类型删除了已声明默认值的属性的可选标志
3.defineEmits
- 子组件通过事件向父组件发送数据。
vue
<!-- Child.vue -->
<script setup>
const emit = defineEmits(['sendMsg']);
emit('sendMsg', 'Hello Parent');
</script>
<!-- Parent.vue -->
<Child @sendMsg="handleMsg" />
4.defineExpose
- 子组件声明它所暴露的内容,父组件只能访问所暴露的内容。
js
const keyword = ref('');
const changeSelect = () => {};
defineExpose({
keyword,
changeSelect,
});
二、跨组件通信
1.mitt
到 Vue 3 中,$on
、$off
和 $once
方法已从 Vue 实例中完全删除,而且 vue3 中没有 Vue 构造函数,也就没有 Vue.prototype
以及组合式 API 写法没有 this
。因此,要使用 eventBus
模式,就需要安装一个外部事件发射器和监听器包,常用的比如 mitt。
js
// bus.js
import mitt from 'mitt'
export const bus = mitt()
// A.vue
bus.emit('customEvent', value)
// B.vue
bus.on('customEvent', val => { ... })
2.依赖注入:provide + inject
类似于 React 中的 Context,只是用起来比 React 稍微简单点而已,主要用在跨层级组件通信传值~
provide()
接受两个参数:第一个参数是要注入的 key,可以是一个字符串或者一个 symbol,第二个参数是要注入的值。- 当使用 TypeScript 时,key 可以是一个被类型断言为
InjectionKey
的 symbol。InjectionKey
是一个 Vue 提供的工具类型,继承自Symbol
,可以用来同步provide()
和inject()
之间值的类型。
与注册生命周期钩子的 API 类似,provide()
必须在组件的 setup()
阶段同步调用。
VUE
<script setup>
import { ref, provide } from 'vue'
import { fooSymbol } from './injectionSymbols'
//提供静态值
provide('foo', 'bar')
// 提供响应式的值
const count = ref(0)
provide('count', count)
// 提供时将 Symbol作为 key
provide(fooSymbol, count)
</script>
取值:
VUE
<script setup>
import { inject } from 'vue'
import { fooSymbol } from './injectionSymbols'
// 注入不含默认值的静态值
const foo = inject('foo')
// 注入响应式的值
const count = inject('count')
// 通过 Symbol 类型的 key 注入
const foo2 = inject(fooSymbol)
// 注入一个值,若为空则使用提供的默认值
const bar = inject('foo', 'default value')
//注入一个值,若为空则使用提供的函数类型的默认值
const fn = inject('function', () => {})
//注入一个值,若为空则使用提供的工厂函数
const baz = inject('factory', () => new ExpensiveObject(), true)
</script>