JS设计模式—手写发布订阅模式实现跨组件通信

153 阅读1分钟

一、 什么是发布-订阅模式

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

  • 订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),当发布者(Publisher)发布该事件(Publish Event)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码

  • Vue3中不能再以New Vue来当做全局事件总线的处理方式,官方提供了mitt插件来替代New Vue实现全局跨组件通信,这里讲的是第二种方法,手写发布订阅模式来解决跨组件通信。

二、 实现思路

// 在utils文件夹下创建一个EventBus.js文件
const Bus = {
  // map: 存储事件队列的 map, 每个事件都有一个单独的队列,存放所有的事件处理函数
  map: new Map(),
  // on: 订阅事件的方法,根据传入的 eventName 事件名,将handler追加到新建或存在的事件队列中
  on(eventName, handler) {
    const handlers = this.map.get(eventName)
    if (handlers) {
      handlers.push(handler)
    } else {
      this.map.set(eventName, [handler])
    }
  },
  // emit: 触发事件的方法,根据传入事件名称、参数遍历事件队列并触发事件
  emit(eventName, args) {
    const handlers = this.map.get(eventName)
    // console.log(handlers)
    if (!handlers) {
      throw new Error(`${eventName} is not exist`)
    }
    handlers.forEach((handler) => {
      handler(args)
    })
  },
  // off: 取消事件订阅,根据事件名和处理函数取消事件订阅,如不传入处理函数,则清空相应的事件队列
  off(eventName, handler) {
    if (!handler) {
      this.map.set(eventName, [])
      return
    }
    const handlers = this.map.get(eventName)
    const index = handlers.indexOf(handler)
    if (index >= 0) {
      handlers.splice(index, 1)
    }
  },
  // once: 执行单次事件订阅,触发后自动清除订阅
  once(eventName, handler) {
    const tempHandler = (args) => {
      this.off(eventName, tempHandler)
      handler(args)
    }
    this.on(eventName, tempHandler)
  },
}

export default Bus

在组件中使用

// 需要被订阅的组件
import $bus from '@/utils/EventBus.js'
//组件实例挂载完毕订阅once方法(只触发一次)
onMounted(()=>{
	$bus.once('factData',updataFn)
})
// 发布时会触发该方法
let updataFn=(item)=>{
       // 通信的数据
       console.log(item)
}



//需要发布的组件
import $bus from '@/utils/EventBus.js'
let clickLayout=()=>{
	$bus.emit('factData',需要传递的参数)
}