Vue3开发极简入门(16):祖孙组件间通信之provide&inject
前文说了Vue的组件间关系,有父子、爷孙、其他关系。例如之前的Father、Son是父子关系,App与Son就是爷孙关系。而props的Son,与emits的Son,就是其他关系。
前文的props是父传子,emits是子传父。靠这俩,如果想实现爷孙组件间通信,比较麻烦,爷通过props到父,父通过props到子;子通过emits到父,父通过emits到爷。如果最外到最里面的组件,中间隔的N个组件,都这么过桥一下,就比较恶心了。
这种情况下,可以用provide&inject。
爷组件,Grandpa.vue:
<template>
<div class="grandpa">
<h1>爷组件</h1>
司机:{{ driverName }}<br />
发车时间:{{ departTime }}
<Father />
</div>
</template>
<script lang='ts' setup name='grandpa'>
import Father from './Father.vue';
import { provide, ref, type Ref } from 'vue';
const departTime = ref('待定')
const driverName = ref('无')
function setDriver(val: string) {
driverName.value = val
}
provide<Ref<string>>('departTime',departTime)
provide<Function>('setDriver',setDriver)
</script>
<style scoped>
.grandpa {
background-color: aquamarine;
box-shadow: 0 0 10px black;
border-radius: 10px;
height: 100%;
}
</style>
父组件,就是导入了Son.vue,其他什么代码都没有:
<template>
<div class="father">
<h1>父组件</h1>
<h2>整件事情,雨我无瓜~~</h2>
<Son />
</div>
</template>
<script lang='ts' setup name='Father'>
import Son from './Son.vue';
</script>
<style scoped>
.father {
background-color: darkseagreen;
padding: 10px;
margin-top: 10px;
box-shadow: 0 0 10px black;
border-radius: 10px;
width: 90%;
margin: auto;
}
</style>
孙组件:
<template>
<div class="son">
<h1>孙组件</h1>
发车时间:{{ departTime }} <br>
<button @click="letsGo">发车上报</button>
</div>
</template>
<script lang='ts' setup name='Son'>
import { inject, type Ref, ref } from 'vue';
const departTime = inject<Ref<string>>('departTime', ref(""))
const setDriver = inject<Function>('setDriver', (params: string) => { })
function letsGo() {
departTime.value = "2024-01-01 11:11:11." + Math.random()
setDriver('司机A')
}
</script>
<style scoped>
.son {
background-color: silver;
padding: 10px;
margin-top: 10px;
box-shadow: 0 0 10px black;
border-radius: 10px;
width: 80%;
margin: auto;
}
</style>
代码都很好理解,祖代组件定义的属性、Function,用
provide('key名',属性或方法)
提供出去。
后代组件用
inject('key名')
注入接收。
这里有几个要注意的:
- 祖代提供的属性如果不是响应式数据,在后代这里被视为常数,无法修改。使用响应式数据,后代修改了,祖代也会同步变化。
- 网上很多资料这么写:
const test=inject('test');
const testFn=inject('testFn')
但是在使用时,IDE会报类型为未知。解决方法有:
- 注入时给默认值:
const test=inject('test',ref('默认值'));
const testFn1=inject('testFn1',() => {});
const testFn2=inject('testFn2',(param:type) => {}
- 类型断言:
const test=inject('test') as Ref<string>;
const testFn=inject('testFn') as Function
这种写法就是开发人员非常确定传入的类型一定是as的这个,让TypeScript放心大胆的去用。
- 显示指定类型信息,并加上默认值,就是本文代码的写法,这样能保持代码的类型安全性和可维护性。其实provide不用显示指定类型信息也可以,但是带上了,代码的可读性可维护性就更好了。