分享Vue3组件库搭建(3):子父组件是如何通信的,Radio与RadioGroup,Tab与TabItem等实例
前言
每当问起vue的通信方式,常常有人解释最不清晰的,就是provide与inject。有人提到写组件库的时候才会用到,具体怎么用,再深入就不太了解。这就是本文的重点,也是vue组件库的通信神器,无论vue2还是vue3,都给组件库的带来的偌大的方便。
本文重点,组件的通信,不仅仅是provide与inject。如vue2还需结合dispatch与broadcast等来完成子父通信,本文的vue3也需要结合Emitter等来辅助。我们逐步分析。
需求分析
vue2子父通信,想了解可以参考iview: github.com/iview/iview…
本章重点vue3,我们先分析一下组件库的通信需要通信些什么,这里我们用Radio与RadioGroup的来举例。
直接看图例:
实现重点
步骤1:搭建Emitter数据中心
根据图例,我们需要搭建一个Emitter事件总程,来记录所有的子父组件绑定的key。
思路也很简单,就是每个父组件注册一个唯一标识key,子组件找到这个key就可以绑定关系。
import { reactive, ref } from "vue"
export class Emitter {
// 响应式的数据中心
private state = reactive({})
private events: any = ref({})
// 用于注册事件
on(eventName: string, eventHandle: Function) {
this.events[eventName] = eventHandle
// console.log(`this.event.on====`, this.events)
}
// 删除事件
off(eventName: string) {
if (this.events[eventName]) {
delete this.events[eventName]
}
}
// 触发事件
emit(eventName: string, ...rest: any[]) {
// console.log(`this.event.emit====`, this.events)
if (this.events[eventName]) {
this.events[eventName](...rest)
}
}
}
而父组件注册一个唯一标识key也很简单:
export const emitterKey = Symbol()
步骤2:利用provide与inject绑定父子关系
步骤1父组件生成了emitterKey,那么子组件如何找到了。这里的子组件,不单单是指的是children, 可能是children的children的children。所以,此时我们的provide与inject派上用场。
首先,父组件provide这个Key值:
const emitter = new Emitter()
provide(emitterKey, emitter, acceptRadioGroup, provide)
子组件需要认这个父组件, 首先找到父组件
import { Emitter } from "../common/emitter"
import { emitterKey } from "./radio-group/radio-group.vue"
const emitterInject = inject<Emitter>(emitterKey, null)
此时,如果子组件对应的父组件不为空,emitterInject已经可以拿到父组件暴露的方法。我们使用他绑定关系:
父组件,我们新建一个数组来储存子组件的每个方法:
const stateComp = reactive({
groupValue: props.value,
updateRadioStatusFnList: [] as any[],
})
emitter.on("acceptRadioFn", (updateFunc: any) => {
stateComp.updateRadioStatusFnList.push(updateFunc)
})
子组件:
emitterInject && emitterInject.emit("acceptRadioFn", stateComp.parentUpdate)
此时,子组件的updateRadioStatusFnList,记录了所有的子组件更新方法。
步骤3:父组件触发所有子组件变化
上述已经记录了updateRadioStatusFnList。此时如果父组件想修改所有子组件的变化,也很简单:
//通知对应所有的编辑状态关闭
stateComp.updateRadioStatusFnList.map(fn => {
fn(stateComp.groupValue)
})
这样,步骤2记录的所有方法,父组件就可以一步完成。
走到这里,我们就可以完成常用的一些场景。如:
radioGroup修改成“不可编辑”时,通知所有的radio不可点击。
步骤4:子组件变化,通知所有的同类组件变化
那么此时,一个radio给选中了,我要告诉radioGroup下其他的同类radio,都变成不可选中。如何处理?
首先,所有的子组件,重复2~3步骤,需注册到父级监听中:
const acceptRadioGroup = inject<Function>("acceptRadioGroup", null)
const stateComp = reactive({
groupValue: inject("xRadioGroupValue"),
select: () => {
acceptRadioGroup(props.name)
},
parentUpdate: (value: any) => {
if (stateComp.groupValue !== value) {
stateComp.groupValue = value
}
}
})
父组件:
const acceptRadioGroup = (name: string) => {
stateComp.groupValue = name
emit("update:value", name)
stateComp.setVisableList() //同步所有的子选项
}
结语
此时,radioGroup与radio的关系,已经可以达到互相通知:
-
- radioGroup知道自己所有的radio子组件
-
- radioGroup可以同时修改自己的所有的radio子组件
-
- 任意一个radio, 可以触发radioGroup,修改所有子组件的。
理解完成这里的通信,我们的tab与tabItem实例,form与formItem实例, 相信都不是问题。
源码链接
Radio与RadioGroup,Tab与TabItem源码已上传,Form期待下一篇
github: github.com/zhuangweizh…
转载自:https://juejin.cn/post/7108743154370609182