发布订阅模式理解与应用

855

js中的发布订阅

1.什么是发布订阅

比如:你喜欢一部动漫,但是他的更新时间是不确定的,那么你就可以订阅这个动漫的更新时间,每当动漫更新时会发布一条消息,这个时候因为你订阅这个动漫的更新时间,你就会立马收到这条消息,然后第一时间去看动漫了。例如:vue中的事件总线,$emit$on, $on用来订阅事件,$emit用来发布订阅的事件,这样子每当$emit发布订阅的事件时,就会执行$on之前订阅的事件。

2.发布订阅的定义

发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。

订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),当发布者(Publisher)发布该事件(Publish Event)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。 例如上面的例子,动漫就是一个发布者,你就是一个订阅者,订阅更新时间就是把事件注册到调度中心,当动漫更新后,动漫就是发布这个更新时间到调度中心,订阅更新时间这个事件就会被触发。

3发布订阅的实现

  • list是一个调度中心(缓存所有的订阅了的函数列表)
  • on方法用来订阅事件到list中去,通过key来缓存不同的事件
  • emit方法用来发布事件,通过arguments拿到发布事件时传递过来的key并拿到list中的函数,在传递参数去执行函数
  • remove方法用来移除list中订阅的函数
  • once方法只执行一次发布订阅后就移除订阅的函数
let event = {
      list: {},
      // 订阅事件
      on (key ,fn) {
        // 如果没有这个key 就给他赋值为一个数组
        if (!this.list[key]) {
          this.list[key] = []
        }
        // 缓存列表里面加函数
        this.list[key].push(fn)
      },
      // 发布事件
      emit () {
        // 通过arguments取到第一个传递的参数key
        let key = [].shift.call(arguments) // arguments是类数组没有shift方法,用这种call的方式传递参数给前面的数组
        let fns = this.list[key]
        fns.forEach(fn => {
		  //arguments shift方法会改变arguments的长度,剩余的值就是函数所需要的参数
          fn.apply(this, arguments)
        })
      },
      // 移除事件
      remove (key ,fn) {
        let fns = this.list[key]
        // 如果缓存列表没有函数直接返回
        if (!fns) return false
        if (!fn) {
          fns && (fns.length = 0) // .length = 0 清空数组
        } else {
          fns.forEach((cb, i) => {
            if (cb === fn) {
              fns.splice(i, 1)
            }
          })
        }
      },
      // once
      once (key, fn) {
        let on = function () {
          fn.apply(this, arguments)
          this.remove(key, on)
        }
        this.on(key, on)
      }
    }
    // console.log(event)
    let fn = function (data, data1) {
      console.log(data, data1)
    }
 
	event.on('get', fn)
    event.once('post', fn)
    event.emit('get', 1, 'a') // 1, a
    event.emit('get', 2, 'b') // 2, b
    event.emit('post', 3, 'c') // 3, c
    event.emit('post', 4, 'd') // 不执行
    event.remove('get', fn)
    event.emit('get', 5, 'e') // 不执行

4.如何使用

在微信小程序开发中,你需要兄弟组件(a, b)之间进行联系,如果业务需求为:点击a组件的中的一个按钮,b组件中触发一个事件,这个时候就可以用到发布订阅模式,先在b组件中的一个生命周期(onLoad, onReady等)里订阅一个事件为tapB,然后在a组件中点击按钮时就发布这个tapB事件,这个时候b组件中订阅的事件就会执行了。