实现一个发布订阅

更新于 2018-04-09  约 4 分钟

在常用的MVVM框架比如vue,组件间通信可能会有以下三种情况:

1. 父子通信:通过props
2. 非父子组件组件用eventBus通信
3. 如果项目很大,数据需要共享到多个组件(跨组件通信,改同一个数据等),用vuex管理

那么文中的eventBus是怎么实现的呢,就是根据发布订阅模式。发布订阅模式应用广泛,js事件就是一个经典的发布订阅模式。看个例子addEventListener

EventTarget.addEventListener() 方法将指定的监听器注册到 EventTarget 上,当该对象触发指定的事件时,指定的回调函数就会被执行,事件目标可以是一个文档上的元素 Document 本身。
target.addEventListener(type, listener, options);
//type 表示事件类型(eg:click)
//listener 回调函数,当监听的事件类型触发时,执行listener函数
//options 冒泡还是捕获等参数

原生js中,我们通过addEventListener注册事件(订阅),比如鼠标点击,传入回调函数(在注册的事件触发时要执行的函数),那么鼠标点击时(发布),传入的回调函数就会执行。除了原生js事件。

那一个简单的发布订阅模式怎么实现呢?定义一个EventEmitter类,它有以下几个元素:

  • 单例对象,维护订阅者:单例模式就是为保证不同的人实例化EventEmitter类之后,拿到的状态是同一个。
  • 订阅方法:传入订阅的事件类型及回调函数,订阅事件
  • 发布方法:传入发布的事件类型及参数(传给回调函数的),发布事件。发布时,会执行订阅时传入的回调函数
  • 移除方法:移除订阅(监听)的回调函数

代码如下:

class EventEmitter {
            constructor(){
                //单例模式
                this._events = this._events || new Map();
            }
            //发布(触发事件)
            emit(type, ...args){
                //拿到订阅者的回调函数
                var handler = this._events.get(type);
                if(!handler){
                    return;
                }
                
                //发布时,依次执行订阅者的回调
                for(var i = 0; i< handler.length; i++){
                    handler[i].apply(this, args);
                }
            }
            //订阅(监听事件)
            addListener(type, func){
                var handler = this._events.get(type);
                
                //如果还没有同类型的订阅,新创建一个
                if(!handler){
                    this._events.set(type, [func]);
                    return;
                }
                //把回调函数塞入数组
                handler.push(func);
            }
            //移除
            removeListener(type, func){
                var handler = this._events.get(type);
                if(!handler){
                    return;
                }
                
                for(var i = handler.length; i>0; i--){
                    //找到自己要移除的函数,匿名函数不能移除
                    if(handler[i] == func){
                        handler.splice(i, 1);
                    }
                } 
            }
        }

该实现只是探究原理,异常处理之类的都没做。想了解单例模式可以看:《javaScript设计模式与开发实践》(我还没看完emmm)

参考:

[eventBus实现][2]

阅读 1.6k更新于 2018-04-09

推荐阅读
目录