手写一个简单的EventBus

122 阅读1分钟

eventBus事件总线,用于从子组件向父组件发送消息,类似于消息发布--订阅模式。

// eventBus.js
class EventBus {
    constructor() {
        this.events = {} // 用于存放注册的事件,数据用键值对的方式存放
        /**
            key1: Array[fun1, fun2...] // 存放 key1事件注册的所有回调函数,
            key2: Array[fun1, fun2...]
        */
        // 别问,问就是使用对象存储数据结构比较灵活,方便数据的管理和操作
    }
    
    // 订阅消息方法
    $on(event, callback) {
        // 如果事件不存在,就注册新事件
        // let ev = this.events[event] 这样写会有意想不到的结果
        if (!this.events[event]) {
            this.events[event] = []
        }
        // 存在就push回调函数数组中
        this.events[event].push(callback)
    }
    
    // 发布消息方法
    $emit(event, ...params) {
        // 如果事件存在,依次遍历该事件下绑定的所有事件
        if (this.events[event]) {
            // 确保事件的完整性和一致性,循环遍历所有的事件 
            this.events[event].forEach(fn => fn(...params))
        }
    }
    
    // 取消消息订阅方法
    $off(event, callback) {
        // 事件不存在直接return
        if (!this.events[event]) return
        // 如果没有传入callback,则默认删除该事件下所有的回调函数
        if (!callback) {
            delete this.events[event]
            return
        }
        
        // 删除指定的callback
        this.events[event] = this.events[event].filter(cb => cb !== callback)
    }
}

// 使用方法
const eventBus = new EventBus()

// 发布消息
eventBus.$emit('sayHi', 'Jack')

// 订阅消息
eventBus.$on('sayHi', (name) => {})

// 取消订阅, callback可选
eventBus.$off('sayHi', [callback])

在Vue项目中使用,需要注意在父子组件通信中,不能分别使用import EventBus from 'eventBus.js',可能是因为在let eventBus = new EventBus()时,他们不是同一个实例。

// Person.vue
<template>
    <List :eventBus="eventBus" />
</template>
<script setup>
import { onMounted } from 'vue'
import List from './components/List/index.vue'
import EventBus from './utils/eventBus.js'

const eventBus = new EventBus()

onMounted(() => { // 同时也要考虑父组件订阅消息的时机
    eventBus.$on('getVal', (val) => {
        console.log(val);
    })
})
</script>

// List.vue
<template>
    <button @click="onClick">向父组件发送信息</button>
</template>

<script setup>

const props = defineProps({
    eventBus: {
        type: Object,
        required: true,
    }
})
const onClick = () => {
    props.eventBus.$emit('getVal', 'List 组件')
}
</script>

分享一些学习的笔记。🙂