1. 什么是事件中心(个人理解)
我们都知道在vue2/3以及react或者等等其它的框架...都有事件中心的存在,事件中心的作用也是帮助我们在组件中进行传值。通过一个进行派发一个进行监听,来完成组件之间的传值。
2. 大概原理
- 由于本章只实现一个简易版的事件中心,那我接下来仅仅代表着我个人的看法进行阐述我自己的观点,如有不对的地方欢迎jy们进行指正。
- 下面简易写了一个思维导图
- 大概意思就我们在$on的时候需要将传递进来的函数进行收集起来,然后在$emit派发到了指定事件的时候,将这个事件名所对应存储的回调函数进行调用一波,至于$off就是将指定事件对应存储的回调函数进行删除,或者移除其中某一个回调函数。
3. 上代码
- 本次使用ts进行编写,我们先来定义一下类型
export interface EventBusType {
$on: (funName: string, callback: (...args: any []) => void) => void;
$off: (funName: string, callback?: (...args: any []) => void) => void;
$emit: (funName: string, ...args: any []) => void;
}
- 然后编写一个类,这个类进行实现EventBusType这个接口,然后定义一个私有的成员变量list来进行存储收集的函数名与数据,相较于object而言,map进行存储是非常干净的,在此不进行过多的赘述。
import type { EventBusType } from "@/types/event";
class EventBus implements EventBusType {
private list: Map<any, Array<Function>> = new Map();
$on(funName: string, callback: (...args: any[]) => void) {}
$off(funName: string, callback?: ((...args: any[]) => void) | undefined) {}
$emit(funName: string, ...args: any[]) {}
}
- 首先进行数据的收集先来编写一下$on这个函数
$on(funName: string, callback: (...args: any[]) => void) {
// 先进行获取一次已经收集的函数数组,如果没有则赋值为空数组, ??空值合并针对于 undefined | null
const tempList = this.list.get(funName) ?? [];
// 收集本次调用传进来的回调函数
tempList.push(callback);
// 重新进行set
this.list.set(funName, tempList);
}
- 然后编写$emit这个函数
$emit(funName: string, ...args: any[]) {
// 获取已经收集到函数数组
const callbackList = this.list.get(funName);
// 如果存在那么进行依次调用,并且将值进行传递进去
if (callbackList) {
callbackList.forEach((callback: Function) => callback(...args));
}
}
4. 先调用一次看看结果如何
- 创建one.vue 和 two.vue 两个文件然后放入到index.vue文件中进行调用
- index.vue
<template>
<div>
<h2>演示简易版事件中心</h2>
<One />
<Two />
</div>
</template>
<script lang='ts' setup>
import One from './modules/one.vue'
import Two from './modules/two.vue'
</script>
- 分别在one和two两个文件中引入我们编写的事件中心,然后进行调用
<template>
<div>
<button @click="sendData">向兄弟组件传值</button>
</div>
</template>
<script lang='ts' setup>
import event from '@/plugin/event';
const sendData = () => {
event.$emit('sendData', 123456)
}
</script>
<template>
<div>
</div>
</template>
<script lang='ts' setup>
import event from '@/plugin/event';
import { onMounted } from 'vue'
event.$on('sendData', (num) => {
console.log('我是two组件接收到了one组件传递的值===>11行', num);
})
onMounted(() => {
event.$on('sendData', (num) => {
console.log('我是two组件接收到了one组件传递的值===>16行', num);
})
})
</script>
- 调用结果,也是没有任何问题,在two组件获取到了one组件传递过来的参数,对于这个执行顺序则是因为vue3生命周期的原因,这里不做过多的赘述。
- 接下来编写$off函数,分两种情况,第一种是移除所有对应的数据,第二种是移除指定的函数。关于移除指定的函数我们先来进行编写一段代码
接下来我们进行指定函数移除就是使用这种思路进行编写。
$off(funName: string, callback?: ((...args: any[]) => void) | undefined) {
// 移除指定函数
if (callback) {
let tempList = this.list.get(funName) ?? [];
if (tempList.length > 0) {
tempList = tempList.filter((item) => item !== callback);
// 移除了之后重新进行set
this.list.set(funName, tempList);
}
return;
}
// 将指定事件名对应的收集的函数进行删除
this.list.delete(funName);
}
由于删除全部的不方便进行录屏演示,我们来演示一下删除指定函数的 首先将two.vue组件的代码进行变更
<template>
<div>
</div>
</template>
<script lang='ts' setup>
import event from '@/plugin/event';
import { onMounted } from 'vue'
const handler = (num: number) => {
console.log('我是two组件接收到了one组件传递的值===>handler函数执行11行', num);
}
event.$on('sendData', (num) => {
console.log('我是two组件接收到了one组件传递的值===>15行', num);
})
onMounted(() => {
event.$on('sendData', handler)
})
</script>
结果执行下来依然是没有问题的
我们使用一下$off
<template>
<div>
<button @click="rmHandler">移除handler</button>
</div>
</template>
<script lang='ts' setup>
import event from '@/plugin/event';
import { onMounted } from 'vue'
const handler = (num: number) => {
console.log('我是two组件接收到了one组件传递的值===>handler函数执行11行', num);
}
const rmHandler = () => {
event.$off('sendData', handler)
}
event.$on('sendData', (num) => {
console.log('我是two组件接收到了one组件传递的值===>15行', num);
})
onMounted(() => {
event.$on('sendData', handler)
})
</script>
在上面已经演示过移除之前的场景了下面是移除之后进行打印的结果
可以很清楚的看到移除之前还是两个地方进行打印,移除之后就只有一个进行了打印,说明移除指定函数成功。
结尾
到这里这期分享就到此结束了,欢迎jy们多多指正。