发布订阅模式笔记

什么是发布订阅模式

发布-订阅模式是一种对象间的一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于他的对象都将得到状态改变的通知。
订阅者(Subscriber) ——>注册——>调度中心(Event Channel)
发布者(Publisher) ——>发布——>调度中心

简单的例子
 let matchStore = {} // 剧本杀店
 matchStore.playerList = [] // 组队群 

 // 订阅
matchStore.on = function(fn){
  this.playerList.push(fn)
}

// 发布
matchStore.emit = function(){
  // 遍历通知到订阅者
  for (let i = 0; i < this.playerList.length; i++) {
     this.playerList[i].apply(this, arguments)
  }
}
// 玩家1 
matchStore.on(function(name,type,num){
  console.log(`玩家1接受到组队消息:${name}:是一个${type},${num}人本`)
})
// 玩家1 
matchStore.on(function(name,type,num){
  console.log(`玩家2接受到组队消息:${name}:是一个${type},${num}人本`)
})
// 玩家1 
matchStore.on(function(name,type,num){
  console.log(`玩家3接受到组队消息:${name}:是一个${type},${num}人本`)
})

matchStore.emit('声声慢','情感沉浸',6)

这样玩家都能收到订阅信息,但是玩家1只想玩欢乐本,不想收到情感沉浸本的组队通知,这时,我们来改造一哈

let matchStore = {} // 剧本杀店
 matchStore.playerList = [] // 组队群 

 // 订阅
matchStore.on = function(key,fn){
  if(!this.playerList[key]){
    this.playerList[key] = []
  }
  this.playerList[key].push(fn)
}

// 发布
matchStore.emit = function(){
  // 取出事件
  let event = Array.prototype.shift.call(arguments)
  // 拿到事件应的方法列表
  let  fns = this.playerList[event]
  // 遍历通知到订阅者
  if(fns && fns.length >0){
    for (let i = 0; i < fns.length; i++) {
      fns[i].apply(this, arguments)
    }
  }

}
// 玩家1 
matchStore.on('happy',function(name,type,num){
  console.log(`玩家1接受到组队消息:${name}:是一个${type},${num}人本`)
})
// 玩家2 
matchStore.on('happy',function(name,type,num){
  console.log(`玩家2接受到组队消息:${name}:是一个${type},${num}人本`)
})
// 玩家3
matchStore.on('emotion',function(name,type,num){
  console.log(`玩家3接受到组队消息:${name}:是一个${type},${num}人本`)
})

matchStore.emit('happy','宿醉','欢乐本',6)
matchStore.emit('emotion','涂川','情感沉浸',6)

打印结果为:

玩家1接受到组队消息:宿醉:是一个欢乐本,6人本
玩家2接受到组队消息:宿醉:是一个欢乐本,6人本
玩家3接受到组队消息:涂川:是一个情感沉浸,6人本

我们发布happy类型的剧本时,只有玩家1,2 收到了订阅信息。

vue中的发布订阅

大概思路是一样的

function eventsMixin (Vue) {
    var hookRE = /^hook:/;
    Vue.prototype.$on = function (event, fn) {
        var this$1 = this;

        var vm = this;
        // event 为数组时,循环执行 $on
        if (Array.isArray(event)) {
            for (var i = 0, l = event.length; i < l; i++) {
                this$1.$on(event[i], fn);
            }
        } else {
            (vm._events[event] || (vm._events[event] = [])).push(fn);
            // optimize hook:event cost by using a boolean flag marked at registration 
            // instead of a hash lookup
            if (hookRE.test(event)) {
                vm._hasHookEvent = true;
            }
        }
        return vm
    };

    Vue.prototype.$once = function (event, fn) {
        var vm = this;
        // 先绑定,后删除
        function on () {
            vm.$off(event, on);
            fn.apply(vm, arguments);
        }
        on.fn = fn;
        vm.$on(event, on);
        return vm
    };

    Vue.prototype.$off = function (event, fn) {
        var this$1 = this;

        var vm = this;
        // all,若没有传参数,清空所有订阅
        if (!arguments.length) {
            vm._events = Object.create(null);
            return vm
        }
        // array of events,events 为数组时,循环执行 $off
        if (Array.isArray(event)) {
            for (var i = 0, l = event.length; i < l; i++) {
                this$1.$off(event[i], fn);
            }
            return vm
        }
        // specific event
        var cbs = vm._events[event];
        if (!cbs) {
            // 没有 cbs 直接 return this
            return vm
        }
        if (!fn) {
            // 若没有 handler,清空 event 对应的缓存列表
            vm._events[event] = null;
            return vm
        }
        if (fn) {
            // specific handler,删除相应的 handler
            var cb;
            var i$1 = cbs.length;
            while (i$1--) {
                cb = cbs[i$1];
                if (cb === fn || cb.fn === fn) {
                    cbs.splice(i$1, 1);
                    break
                }
            }
        }
        return vm
    };

    Vue.prototype.$emit = function (event) {
        var vm = this;
        {
            // 传入的 event 区分大小写,若不一致,有提示
            var lowerCaseEvent = event.toLowerCase();
            if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
                tip(
                    "Event \"" + lowerCaseEvent + "\" is emitted in component " +
                    (formatComponentName(vm)) + " but the handler is registered for \"" + event + "\". " +
                    "Note that HTML attributes are case-insensitive and you cannot use " +
                    "v-on to listen to camelCase events when using in-DOM templates. " +
                    "You should probably use \"" + (hyphenate(event)) + "\" instead of \"" + event + "\"."
                );
            }
        }
        var cbs = vm._events[event];
        if (cbs) {
            cbs = cbs.length > 1 ? toArray(cbs) : cbs;
            // 只取回调函数,不取 event
            var args = toArray(arguments, 1);
            for (var i = 0, l = cbs.length; i < l; i++) {
                try {
                    cbs[i].apply(vm, args);
                } catch (e) {
                    handleError(e, vm, ("event handler for \"" + event + "\""));
                }
            }
        }
        return vm
    };
}

/***
   * Convert an Array-like object to a real Array.
   */
function toArray (list, start) {
    start = start || 0;
    var i = list.length - start;
    var ret = new Array(i);
    while (i--) {
          ret[i] = list[i + start];
    }
    return ret
}

你可能感兴趣的:(发布订阅模式笔记)