Typescript自定义事件系统

文章介绍了如何通过事件管理系统设计,包括设置中间对象管理监听者和发送者,使用字典存储事件和节点关系,以及实现粘性通知以确保通知到位。代码示例展示了EventInfo和EventManager类的实现细节。
摘要由CSDN通过智能技术生成

基本原理

在事件传递的过程中设置一个中间对象,负责管理监听者和发送者,以达到解耦合的目的。

首先我们定义一个方法来注册监听对象,该对象包含节点(即作用域)、事件名和事件函数,然后我们在事件触发的时候调用触发函数,来触发所有监听该事件的对象。触发函数包含事件名和参数。

此处我们以字典 _eventDic 来存储监听对象,以事件名作为字典的key,在字典里我们以作用域为key定义另一个字典,防止同名事件冲突以及同作用域事件重复添加。

同时,由于我们需要一个清除节点上所有事件的方法,因此我们单独设置一个字典 _nodeEventDic 用来保存节点和事件的关系。在清除节点上所有事件的时候只需要遍历该字典 _nodeEventDic 中对应作用域(传入的node)的事件并且从事件字典 _eventDic 中查找对应的事件对象及其作用域即可。

具体的调用过程如下图所示:

这就是最基本的事件系统。

粘性通知

为了防止部分情况中出现通知不到位的情况(即先通知后监听),这里我们引入了粘性通知的功能。

此处我们定义一个字典 _stickyArr 来保存通知的内容,这里我们分为两种情况,一种情况是该粘性通知只需要通知一次,则我们只需要让新的通知覆盖旧的通知;另一种是需要多次通知,则我们设置一个数组来保存多次通知的内容。

粘性通知和普通通知的触发事件逻辑相似,可以说前半部分的代码和普通通知一样,只不过我们增设一个通知标识符,用于判断是否通知到位,若没有通知到位,则进入粘性通知内容保存的流程。

注册监听者的部分代码也有改动,在注册完成后,若从粘性通知字典 _stickyArr 取到对应事件名的内容,则立刻进行一次通知,通知结束后删除对应的内容。

简单来说,粘性通知就是在监听和通知这两个过程之后增加一个判断,对于监听者来说,就是要判断有无通知的内容,而对于通知来说,就是判断是否有监听者监听过该通知内容。

代码

事件数据类

export default class EventInfo {

    public node;
    public event: Function;
    public type: string;

    constructor(node, type: string, event: Function) {
        this.node = node;
        this.event = event;
        this.type = type;
    }
}

事件管理类

import EventInfo from "../bean/EventInfo";

export default class EventManager {
    /** 事件id */
    private static _eventId = 1;
    /** 事件字典 类型-节点-事件 */
    private static _eventDic = {};
    /** 节点对应事件 节点-事件 */
    private static _nodeEventDic = {};
    /** 粘性通知字典 */
    private static _stickyArr = {};

    /**
     * 发送监听
     * @param type  监听类型 
     * @param data 发送的数据
     */
    public static dispatchEvent(type, data): void {
        for (let key in this._eventDic[type]) {
            let event = this._eventDic[type][key] as EventInfo;
            event.event.call(event.node, data);
        }
    }

    /**
     * 粘性通知
     * @param type 
     * @param data 
     */
    public static dispatchEventSticky(type, data, isArr?) {
        let eventOk = false;
        //普通通知部分
        for (let key in this._eventDic[type]) {
            let event = this._eventDic[type][key] as EventInfo;
            event.event.call(event.node, data);
            eventOk = true;
        }
        //如果没有执行普通通知,则保存通知内容
        if (!eventOk) {
            if (isArr) {
                if (!this._stickyArr[type]) {
                    this._stickyArr[type] = [];
                }
                this._stickyArr[type].push(data);
            } else {
                this._stickyArr[type] = data;
            }
        }
    }

    /**
     * 注册监听
     * @param node  监听的节点 
     * @param type  监听类型
     * @param event 监听事件
     */
    public static onEvent(node, type: string, event: Function) {
        var obj = new EventInfo(node, type, event);

        if (!this._eventDic[type]) {
            this._eventDic[type] = {};
        }
        //如果节点上没有事件id,则添加
        if (node.eventId == undefined) {
            node.eventId = "event_" + this._eventId++;
        }
        //添加事件
        this._eventDic[type][node.eventId] = obj;
        //获取粘性通知数据
        let data = this._stickyArr[type];
        if (data) {
            //根据是否数组决定是否多次执行事件
            if (data.length) {
                let event = this._eventDic[type][node.eventId] as EventInfo;
                for (let i = 0, len = data.length; i < len; i++) {
                    event.event.call(event.node, data[i]);
                }
                delete this._stickyArr[type];
            } else {
                let event = this._eventDic[type][node.eventId] as EventInfo;
                event.event.call(event.node, data);
                delete this._stickyArr[type];
            }
        }
        //汇总节点上的所有事件
        if (!this._nodeEventDic[node.eventId]) {
            this._nodeEventDic[node.eventId] = {};
        }

        this._nodeEventDic[node.eventId][type] = true;

    }

    /**
    * 清除某一节点上的所有监听
    * @param node 
    */
    public static offAllEventByNode(node) {
        if (!node || !this._nodeEventDic[node.eventId]) return;
        for (let key in this._nodeEventDic[node.eventId]) {
            delete this._eventDic[key][node.eventId];
            if (Object.keys(this._eventDic[key]).length == 0) {
                delete this._eventDic[key];
            }
        }

        delete this._nodeEventDic[node.eventId];
    }
}

GitHub项目地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值