如果感觉文章不错,欢送关注、点赞和分享!
继续分享技术博文,关注微信公众号 前端LeBron
什么是公布 — 订阅模式
公布 — 订阅模式,它定义程序对象之间一种一对多的依赖关系,当一个对象的状态产生扭转时,所有依赖它的对象都将失去告诉并执行相应操作。在日常生活中,常见的公布订阅模式有:订阅号,用户关注订阅号,内容创作者在平台公布内容后,平台遍历粉丝列表进行内容推送;销售中介,客户给销售人员留下了客户信息及联系方式,在新产品推出时,挨个给客户打电话进行采购,等等... 而公布订阅模式,个别由三类对象组成:
发布者 Publisher
- 事件发布者,将须要公布的事件传递给信道中介
信道中介 Event Channel
- 作为公布订阅的中介,须要缓存相应事件的订阅者列表,在发布者公布时遍历订阅者列表并告诉它们
订阅者 Subscriber
- 订阅事件,并定义事件公布后的操作,向信道中介发动订阅
公布 — 订阅模式的利用
在下面也介绍到了公布订阅模式在日常生活中以及前端开发工作中的应用场景,例如原生 DOM 事件,裸露了订阅接口后,开发者不须要重复查问该事件是否实现;异步编程的申请中,开发者无需轮询 HTTP 申请的状态,能够通过订阅申请的 success、error、update 等事件。在编程格调上,公布订阅模式让两个对象在松耦合的状况下建立联系,不再须要显式的将接口调用硬编码耦合进另一个对象,发布者和订阅者产生各自代码的变更都不会影响到对方,上面来看看前端开发最常见的公布订阅模式利用: DOM 事件和自定义事件。
DOM 事件
由简入繁, DOM 事件是前端开发中最常见、简略的公布订阅模式利用
document.body.addEventListener('click', function(){ alert(1);}, false );// 减少订阅者前者也不受影响document.body.addEventListener('click', function(){ alert(2);}, false );document.body.click(); // 模仿用户点击
自定义事件
由上述订阅号为例子实现一个繁难的自定义事件,进阶公布订阅模式利用
const subscription = {};// 订阅者列表subscription.subscriber = [];// 订阅函数subscription.subscribe = function (fn) { this.subscriber.push(fn);};// 公布函数subscription.publish = function () { this.subscriber.forEach((fn) => { fn.apply(this, arguments); });};// 粉丝 1 订阅subscription.subscribe((article) => { console.log(`fans1 receive ${article}`);});// 粉丝 2 订阅subscription.subscribe((article) => { console.log(`fans2 receive ${article}`);});// 订阅号公布const article = 'article'subscription.publish(article);// fans1 receive article// fans2 receive article
通用公布 - 订阅模式
下面自定义事件的例子,仅能局限在单个订阅号场景
上面通过 key 将事件的公布和订阅进行隔离,实现通用的公布订阅模式
// 通用公布订阅模式class EventEmitter { // 订阅者列表 subscriber = {}; // 订阅 subscribe(key, fn) { if (!Array.isArray(this.subscriber[key])) { this.subscriber[key] = []; } this.subscriber[key].push(fn); } // 勾销单个订阅 unsubscribe(key, fn) { const subscribers = this.subscriber[key] || []; this.subscriber[key] = subscribers.filter((_fn) => _fn !== fn); } // 勾销所有订阅 unsubscribeAll(key) { this.subscriber[key] = []; } // 公布 publish(key, ...args) { const subscribers = this.subscriber[key] || []; if (subscribers.length === 0) { console.log("has't subscriber"); } subscribers.forEach((subscriber) => { subscriber.apply(this, args); }); }}// 创立公布订阅信道中介const e = new EventEmitter();// 发布者const publisher = { article1: 'article1', article2: 'article2',};// 订阅者const subscriber1 = (article) => { console.log(`fans1 receive ${article}`);};const subscriber2 = (article) => { console.log(`fans2 receive ${article}`);};// 订阅操作e.subscribe('event1', subscriber1);e.subscribe('event1', subscriber2);// 公布操作e.publish('event1', publisher.article1);// fans1 receive article1// fans2 receive article1// 多事件公布订阅e.subscribe('event2', subscriber1);e.publish('event2', publisher.article1);// fans1 receive article1// 勾销单个订阅e.unsubscribe('event1', subscriber1);e.publish('event1', publisher.article2);// fans2 receive article2// 勾销所有订阅e.unsubscribeAll('event1');e.publish('event1');// has't subscriber
模块间通信
模仿如下场景,在一个中台型平台上能够接入多款利用,切换不同的利用时须要:
- 拉取新的利用信息
- 拉取新的利用设置
- 拉取用户对新利用的权限信息
- 重置系列缓存数据
而这些业务逻辑和操作又封装在各自的 store 或者组件里,如果没有利用公布订阅模式,可能代码时上面这种状况;如果有新增一个切换利用的入口,又须要 CV 一遍同样的代码;
// 控制台页面function openApp(){ userStore.getPermission(); appStore.getAppInfo(); settingStore.getAppSetting(); dataStore.resetDataCache();}
而如果利用了公布订阅模式,只需在各自的模块内执行订阅和公布操作即可,升高模块间的耦合度
如果新增切换利用的入口,也只须要新增一行“公布”操作的代码
// event.js// 创立全局公布订阅信道export const event = new EventEmitter();// 集中保护事件名称 防止命名重瞳export const EVENT_TYPE = { OPEN_APP: 'OPEN_APP',};// user.jsevent.subscribe(EVENT_TYPE.OPEN_APP, getPermission);// app.jsevent.subscribe(EVENT_TYPE.OPEN_APP, getAppInfo);// setting.jsevent.subscribe(EVENT_TYPE.OPEN_APP, getAppSetting);// data.jsevent.subscribe(EVENT_TYPE.OPEN_APP, resetDataCache);// console.jsfunction openApp(){ event.publish(EVENT_TYPE.OPEN_APP);}
观察者模式和公布 — 订阅模式
观察者模式和公布 — 订阅模式最大的区别在于:
- 观察者模式由具体指标(被察看对象)调度
- 公布 — 订阅模式由调度核心(信道中介)对立调度
- 公布 — 订阅模式比观察者模式多一个调度核心(信道中介)
也能够了解为公布 — 订阅模式是基于观察者模式进行通用化设计,涣散耦合,灵便度更高
观察者模式和公布 — 订阅模式的前端实际别离有 Vue 的数据双向绑定和事件总线 EventBus
对 Vue 相干实现感兴趣能够看看以下几篇文章
- Vue的前世今生
- Vue进阶 Diff算法详解
- [倡议珍藏] 你想晓得的Vue3外围源码这里都有
- [源码解读]一文读懂Vuex4源码
小结
浏览完本文,咱们对公布 — 订阅模式有了一个大抵的理解。它的长处非常明显,机会和对象上的解耦,广泛应用于异步编程,同时帮忙咱们实现松耦合的代码编写。但也不是没有毛病,对象与对象之间的必要分割将被深埋在背地,可能会导致程序难以跟踪保护和了解。创立订阅者对象和存储列表须要耗费肯定的工夫和内存,而且存在很多订阅者订阅了一个事件之后,这个事件没有触发,而这块内存却始终被占用。优缺点都有,不要为了用而用,适宜场景和业务的才是最好的。
设计模式系列文章举荐
- JavaScript 设计模式 —— 单例模式
- JavaScript 设计模式 —— 策略模式
- JavaScript 设计模式 —— 代理模式
继续分享技术博文,欢送关注!
- 微信公众号:前端LeBron
- 掘金:前端LeBron
- 知乎:前端LeBron