JS发布订阅模式

介绍

vue 开发中,经常会用到事件传递,例如 eventBus ,我们可以使用 onemit 方法去触发需要的事件.

定义

发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。订阅者( Subscriber )把自己想订阅的事件注册( Subscribe )到调度中心( Event Channel ),当发布者( Publisher )发布该事件( Publish Event )到调度中心,也就是该事件触发时,由调度中心统一调度( Fire Event )订阅者注册到调度中心的处理代码。 例如以下在 vue 中的使用

// 封装成工具库eventBus
import Vue from 'vue'
let bus = new Vue()
Vue.prototype.$eventBus = bus
export default bus

// 页面使用
import Bus from './commom/eventBus'
Bus.$emit('test', null)
Bus.$on('test', this.doSomeThing)

实现

以上是 vue 集成了功能.同样的,我们可以用 es6 实现. ::: warning 思路

  1. 创建一个对象

  2. 在该对象上创建一个缓存列表(调度中心)

  3. on 方法用来把函数 fn都加到缓存列表中(订阅在注册事件到调度中心)

  4. emit 方法取到 arguments 里第一个当做 event ,根据 event 值去执行对应缓存列表中的函数(发布者发布事件到调度中心,调度中心处理代码)

  5. off 方法可以根据 event 值取消订阅(取消订阅) :::

// 代码
let eventEmitter = {
  // 缓存列表
  list: {},
  // 订阅
  on (event, fn) {
    // 如果没有event值,就给event创建个缓存列表
    // 如果有对应的event值,把fn添加到对应event的缓存列表里
    (this.list[event] || (this.list[event] = [])).push(fn)
    return this
  },
  // 监听一次
  once (event, fn) {
    // 先绑定,调用后删除
    function on () {
      this.off(event, on)
      fn.apply(this, arguments)
    }
    on.fn = fn
    this.on(event, on)
    return this
  },
  // 取消订阅
  off (event, fn) {
    let fns = this.list[event]
    // 如果缓存列表中没有对应的fn,返回false
    if (!fns) return false
    if (!fn) {
      // 如果fn不存在,就会将event值对应缓存列表中的fn都清空
      fns && (fns.length = 0)
    } else {
      // 如果有fn, 遍历缓存列表,看看传入的fn与那个函数相同,如果相同就直接从缓存列表中删除
      let cb
      let cbLen = fns.length
      for (let i = 0; i < cbLen; i++) {
        cb = fns[i]
        if (cb === fn || cb.fn === fn) {
          fns.splice(i, 1)
          break
        }
      }
    }
    return this
  },
  // 发布
  emit () {
    // 第一个参数对应的event值,直接用数组的shift方法取出
    let event = [].shift.call(arguments)
    let fns = [...this.list[event]]
    // 如果缓存列表里没有fn就返回false
    if (!fns || fns.length === 0) {
      return false
    }
    // 遍历event值对应的缓存列表,依次执行fn
    fns.forEach(fn => {
      fn.apply(this, arguments)
    })
    return this
  }
}

// 测试
function user1 (content) {
  console.log('用户1订阅了: ', content)
}
function user2 (content) {
  console.log('用户2订阅了: ', content)
}
function user3 (content) {
  console.log('用户3订阅了: ', content)
}
function user4 (content) {
  console.log('用户4订阅了: ', content)
}

// 订阅
eventEmitter.on('article1', user1)
eventEmitter.on('article1', user2)
eventEmitter.on('article1', user3)

// 取消user2方法的订阅
eventEmitter.off('article1', user2)
eventEmitter.once('article2', user4)

// 发布
eventEmitter.emit('article1', 'JS 发布-订阅模式')
eventEmitter.emit('article1', 'JS 发布-订阅模式')
eventEmitter.emit('article2', 'JS 观察者模式')
eventEmitter.emit('article2', 'JS 观察者模式')

参考

Last updated