在前端中观察者通常抽象为事件更具实用性,但这种模式会有一个问题.
假设想在登陆成功后通知组件A、B、C更新view(A、B、C未登录时view处于缺省状态).
-
用观察者模式的话
const ob = new Observable() // A、B、C进行一波订阅 ob.add('update1', () => { console.log('login successful!') }) ob.add('update2', () => { console.log('login successful!') }) ob.add('update3', () => { console.log('login successful!') }) // 通知A、B、C ob.notify('update1') ob.notify('update2') ob.notify('update3')
可以看出,登陆成功后想通知组件A、B、C更新view.前提得三个组件都进行订阅,并发布三次通知,才能实现.
-
希望的做法(猜想模式):
const ob = new Observable() const update = new Type() // 组件a update.add(() => { console.log('login successful 1!') }) // 组件b update.add(() => { console.log('login successful 2!') }) // 组件c update.add(() => { console.log('login successful 3!') }) ob.add(update) ob.notify(update)
-
分析:
现有模式 类比 描述 观察者模式 微商模式 用户A、B、C订阅了商品x(这个商品x的订阅是由店主手动维护的),当商品x到货后,店主需要逐个通知对应的用户(店主需要知道哪些用户订阅了商品x). 猜想模式 淘宝模式 用户A、B、C关注了商品x(这个商品x的订阅是由商品模块自身维护的),当商品x到货后,店主只用点击对应的商品到货通知,所有关注商品x的用户就可以收到通知. -
区别:
通过一个中间层(商品模块自身),让用户和店主进行解耦.店主不需要知道哪些用户订阅了商品x,这个问题由中间层自己处理.而店主只需在对应商品到货时发布对应商品的到货通知就可以达到对所有关注了此商品的用户推送消息.
-
发布订阅模式的引入
-
理解: 淘宝模式的抽象化
-
图解
-
实现:
// 事件总线(调度中心) class EventBus { constructor() { this.subscribers = []; } $on(fn) { this.subscribers.push(fn); } $off(fn) { this.subscribers.forEach((_fn, index) => { fn === _fn && this.subscribers.splice(index, 1); }) } $once(fn) { fn.once = true; this.subscribers.push(fn); } $notify(params) { this.subscribers.forEach(fn => { fn(params); fn.once && this.$off(fn); }); } } // 发布 class Pub { constructor() { this.events = []; } $increase(event) { this.events.push(event); } $broadcast(event, ...params) { this.events.forEach(_event => { event === _event && _event.$notify(...params); }) } } module.exports = { Pub: new Pub(), EventBus };
-
使用
const ev1 = new EventBus(); const ev2 = new EventBus(); // 订阅主题 ev1.$on(data => { console.log(`${data}==>订阅者1收到消息了`); }); ev1.$on(data => { console.log(`${data}==>订阅者2收到消息了`); }); ev2.$on(data => { console.log(`${data}==>订阅者2收到消息了`); }); ev2.$once(data => { console.log(`${data}==>订阅者3收到消息了`); }); // 加入主题 Pub.$increase(ev1); Pub.$increase(ev2); // 发布主题 Pub.$broadcast(ev1, '主题1更新了~'); Pub.$broadcast(ev2, '主题2更新了~'); Pub.$broadcast(ev2, '主题2再次更新了~'); /* 主题1更新了~==>订阅者1收到消息了 主题1更新了~==>订阅者2收到消息了 主题2更新了~==>订阅者2收到消息了 主题2更新了~==>订阅者3收到消息了 主题2再次更新了~==>订阅者2收到消息了 */
-