实现一个简单的EventHub

前言

谈到EventHub就不得不说到发布订阅这一种设计模式,EventHub正是基于这种设计模式实现出来的一个实例,当前在前端方面,EventHub应用已经十分广泛了,例如Vue中的eventbus、redux也对EventHub有所借鉴(redux是单向数据流,遵循MVC结构,M的数据更新后V紧接着更新界面,而EventHub的数据流是没有方向限制的)

什么是EventHub

前言谈到EventHub是基于发布订阅模式实现的一个实例,那么要搞清楚EventHub就先要搞清楚什么是发布订阅模式。

  • 什么是发布订阅模式
    因为发布订阅模式是一个抽象的东西,所以在此我只能以一个例子来做比喻:
    假设你是一个赏金猎人,通过接取各式各样的任务来赚取赏金,但同时你又不认识发布任务的人,而发布任务的人也不知道你这个人的存在,这时候就需要一个工会,工会负责接受委托人发布任务,与此同时也能向赏金猎人提供任务,关系大致如下:


    image.png

    这就相当于委托人发布任务,赏金猎人订阅任务并执行。

  • EventHub是什么,有什么用?
    基于上面的发布订阅模式,我们把赏金猎人设定为模块A,而委托人设定为模块B,那么这个任务中心就是EventHub了,也就是说是一个事件委托中心。
    以此也就不难知道,EventHub就是用来多模块间进行通信的工具,最典型的例子就是Vue中的eventbus。


    image.png

实现一个简单的EventHub

从上面所说的可以得知,EventHub的实现其实就几个功能,发布(on),订阅(emit),当然还有可能存在取消任务的情况(off),这样就足够实现一个简单的EventHub了,不废话,代码如下:

type Event = (data?: any) => any

class EventHub {
    // cache用于存储事件,存储中心
    cache: { [key: string]: Event[] } = {}

    // 接收事件名和事件,同时将其放入到存储中心
    on = (name: string, fn: Event) => {
        this.cache[name] = this.cache[name] || []
        this.cache[name].push(fn)
    }

    // 被订阅的时候,将存储中心对应事件名的所有事件拿出来执行
    emit = (name: string, data?: any) => {
        if(this.cache[name] === undefined) return
        this.cache[name].forEach(fn => {
            fn(data)
        })
    }

    // 取消事件的时候,将被取消的事件从对应事件名中剔除
    off = (name: string, fn: Event) => {
        if(this.cache[name] === undefined) return
        this.cache[name] = this.cache[name].filter(f => f !== fn)
    }
}

接着我们来看看效果:
订阅成功:


image.png

取消订阅:


image.png

推荐阅读更多精彩内容