实现一个简易的可回复广播事件,优雅解决跨组件问题

613 阅读4分钟

在前端的有些场景中,我们需要进行组件间的通信,一般而言,我们可以采用的手段有很多,比如:react的context、redux等等,他们可以跨组件共享数据。如果说我们的通信只是进行数据同步的话,那么现有方案能很好的处理此类问题。

但是,现在我们的场景不涉及数据同步,我们只想向需要我的组件告知我的动作。比如,我们有一个需求如下:

有一个窗口,这个窗口是用来预览各种文件的,并且我们可以对文件进行评论。现在有一个小功能,就是在关闭窗口的时候,需要判断一下有没有未提交的评论,如果有的话,就询问一下,是否需要提交评论?是提交后关闭窗口,还是先不关闭窗口,继续编辑评论

这个小需求其实有多种实现方式

一、第一种的方式,可以把评论数据放到store里面,然后在关闭窗口的组件读取 store 里面的评论数据,根据数据进行判断。

这个方式虽然能实现需求,但是它有几个问题:

  • 关闭组件和评论组件耦合了,在关闭组件中做了评论的业务处理。如果在关闭窗口的时候,还有类似的业务需要处理,那么这个关闭窗口的逻辑将会变得臃肿
  • 二是对于评论数据,这里不需要写到store中,因为他暂时还不是一个需要全局消费的数据

二、第二种方式呢,是在关闭窗口的时候,发出一个广播,如果某个组件需要根据这个关闭事件做一些处理的话,就可以监听这个事件,然后根据自身的业务逻辑,给广播发送源一个结果,广播发送源根据返回结果,来决定执行对应的结果,在这里,广播发送源就是关闭组件了

第二种方式跟第一种相比的话,组件之间不再耦合了,每个组件都只需要关注自己的业务逻辑就好了,比如关闭组件,它只需要根据收到的结果执行关闭或者不关闭,至于这个结果是依据什么业务逻辑得到的,它是不需要关心的。

那么以往我们使用的广播,都是只能监听到相应的事件,广播接收方并不能决定广播发送方的逻辑。今天实现这个小需求的关键就是在基础广播的基础上,加上“回复”功能

先实现一个广播,广播的话,使用发布订阅模式就好了,这里不多说,最基本的三个事件如下:

const eventMap = {}
addEvent(eventName, fn) {
    // 添加事件
    if (eventMap[event]) {
      eventMap[event].push(fn)
    } else {
      eventMap[event] = [fn]
    }
}
sendEvent(eventName, val) {
    // 发送事件
    if (this.eventList[event]) {
      this.eventList[event].forEach((fn) => fn(val))
    }
}
removeEvent(event: string, fn) {
    // 移除事件
    if (!eventMap[event]) return
    eventMap[event] = eventMap[event].filter((f) => f !== fn)
}

以上就是一个基本的广播,如果需要实现一个“可回复”功能的话,那么自然第一个想到的就是使用promise去实现,我们只需要把保存的监听事件,用promise重新包一下就可以了,代码如下:

const sendEvent = (event: string) => {
    if (eventMap[event]) {
      const p = eventMap[event].map((fn) => {
        return new Promise((resolve, reject) => {
          fn({ resolve, reject, payload: val })
        })
      })

      return Promise.all(p)
    }
    // 没有广播,直接成功
    return Promise.resolve()
  }

代码很简单,经过改造以后,发出去的广播结果由各个监听事件来决定,并且发送方,也能收到结果,并根据结果做出自己的逻辑。

再回到文章开头的小例子,来看看使用“可回复的广播”是怎么实现的

addEvent('win-close', (resolve,reject) => {
  if (没有评论){
    resolve()
    return
  }
  
  if (保存评论){
    发起保存请求
    resolve()
  }
})

sendEvent('win-close').then(() => {
  关闭窗口
})

可以看到,关闭窗口这段逻辑,是根据监听事件的结果来决定是否执行的。整个可回复广播大概就是这样了

往期回顾

打造一个优雅的git工作流

怎么自动化处理前后端对接过程?这是一个问题