Open su37josephxia opened 2 years ago
发布订阅模式是一种对象间一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都将得到状态改变的通知。订阅者把自己想订阅的事件注册到调度中心,当发布者发布该事件到调度中心时也就是触发事件时,就会由调度中心统一调度订阅者注册到调度中心的处理代码。 要实现该模式需要两个步骤:
对于事件的监听模式而言,它这里面涉及到三个重要概念:事件+事件监听器+事件源。
const btn = document.getElementById("btn");
btn.addEventListener("click", function (e) {
console.log(e)
}, false);
addEventListener 中的第一个参数 click
就是一个事件,第二个参数就是事件处理程序
上述代码包含几个关键对象:
addEventListener()
的工作原理是将实现 EventListener
的函数或对象添加到调用它的 [EventTarget](https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget)
上的指定事件类型的事件侦听器列表中。也就是说在 btn 这个对象中针对每一种事件类型,都有一个事件侦听器列表,上述代码就是将事件处理函数添加到 btn 对象中 click 类型事件的侦听器列表中。
在发布订阅模式中有三个角色:
Publisher
Event Channel
Subscriber
将这三个角色套用到上面 dom 事件的处理逻辑中:
click
事件添加一个事件处理函数 ,就相当于订阅操作click
事件就相当于发布的操作我理解这里的事件调度中心就是 btn 对象,在 btn 对象中维护着各种事件的侦听器列表;
事件处理函数就相当于订阅者,订阅了这个事件,当事件触发时会通知事件处理函数进行执行;
发布者就是触发事件的对象,可能是用户点击,或者直接执行 btn.onclick()
并且发布订阅是一种一对多的关系,例如我们可以针对 click 事件注册多个事件处理函数,这些函数都会添加到 click 事件的侦听器列表中,当事件触发时依次执行。
事件就是发布订阅模式的一种具体实现,本质就是一种消息通知机制,使得订阅者和发布者能够通过一个第三方渠道进行通信。
事件:
事件是观察者模式,在对象之间定义一个一对多的依赖,当对象自身状态改变的时候,会自动通知给关心该状态的观察者。这种对象与对象,有点像 商家-顾客 的关系。
案例:
document.addEventListener('click',()=>{})
该模式理解起来和观察者模式一样,也是定义一对多的依赖关系,对象状态改变后,通知给所有关心这个状态的订阅者。 订阅发布模式有订阅的动作,可以不和商家直接产生联系,只要能订阅上关心的状态即可,通常利用第三方媒介来做,而发布者也会利用三方媒介来通知订阅者。 这有点像 商家-APP-顾客 的关系。 在程序实现中,第三方媒介称之为 EventBus(事件总线),可以理解为订阅事件的集合,
// 订阅
EventBus.on('custom', () => {})
// 发布
EventBus.emit('custom')
案例:
从概念上理解,两者没什么不同,都在解决对象之间解耦,通过事件的方式在某个时间点进行触发,监听这个事件的订阅者可以进行相应的操作。
在实现上有所不同,观察者模式对订阅事件的订阅者通过发布者自身来维护,后续的一些列操作都要通过发布者完成;订阅发布模式是订阅者和发布者中间会有一个事件总线,操作都要经过事件总线完成。
发布订阅模式有一个事件通道作为调度中心,管理事件的订阅与发布工作,隔绝了订阅者与发布者的依赖关系。订阅者在订阅事件时,只关注事件本身,不关心谁发布这个事件;发布者在发布事件时,只关注事件本身,不关心谁订阅了这个事件
浏览器的事件系统本质就是发布订阅模式的一种实现 事件的订阅发布流程:
从上面说的流程来看,浏览器的事件系统与发布订阅是一致的
事件与订阅发布的关系?
在发布订阅模式中,发布者和订阅者不知道彼此的存在,发布者不能够直接将消息发送给订阅者 可以通过调度中心来维持订阅者和发布者之间的联系; 调度中心也可称为事件总线; 订阅者将订阅的事件注册到调度中心; 发布者发布事件到调度中心; 调度中心统一处理发布者发布的信息并发送给相应的订阅者
发布订阅模式由三部分组成
在 DOM 中的事件的实现本质上就是发布订阅模式:
发布订阅模式中,发布者-事件中心-订阅者,三者的关系是独立的,互不耦合。 订阅者去事件中心订阅自己感兴趣的事情并注册自己的回调,发布者去事件中心通知事件中心发布相关事件,事件发布后,所有的订阅了该事件的订阅者在事件中心注册的回调都会执行。
发布订阅模式在前端应用非常广泛,比如
webpack的tapable钩子订阅就是利用了发布订阅模式,插件去compiler的hooks里订阅相关钩子,并注册自己的回调,当事件执行的时候,所有订阅了该钩子的插件的回调都会执行。
babel在traverse遍历ast的时候,也是利用了发布订阅模式,babel插件去订阅ast的相关节点,当遍历到这个节点的时候,会去通知所有订阅了的babel插件,并执行他们的回调。
两者实现方式是相似的,都是通过收集回调,然后时机到了遍历调用实现。发布订阅是关注和被关注者双方的互动,而事件是有一个控制中心,管理着多个事件,并且只要愿意,是可以串台的,当然也很少这样干。事件的话就比如浏览器的 addEventListener。