带你了解前端设计模式-🍀发布-订阅模式🍀

设计模式是一套被反复使用、多数人知晓、经过分类编目的、代码设计经验的总结。它是为了可重用代码,让代码更容易的被他人理解并保证代码的可靠性。

设计模式合集链接:

带你了解前端设计模式-🍀单例模式🍀

带你了解前端设计模式-🍀工厂模式🍀

带你了解前端设计模式-🍀策略模式🍀

带你了解前端设计模式-🍀代理模式🍀

带你了解前端设计模式-🍀观察者模式🍀

带你了解前端设计模式-🍀发布-订阅模式🍀

概述

发布-订阅是⼀种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。⽽是将发布的消息分为不同的类别,⽆需了解哪些订阅者(如果有的话),同样的,订阅者可以表达对⼀个或多个类别的兴趣,只接收感兴趣的消息,⽆需了解哪些发布者存在。

在23种设计模式中没有发布-订阅模式的,其实它是观察者模式的一个别名,但两者又有所不同。这个别名非常形象地诠释了观察者模式里两个核心的角色要素------发布者和订阅者

我们先看下两者的结构图:

观察者模式

发布-订阅模式

这两种模式的最大区别就是发布-订阅模式有一个调度中心,从两者结构图可以看到,观察者模式是由具体目标调度的,而发布-订阅模式是统一由调度中心调的,所以观察者模式的订阅者与发布者之间是存在依赖的,而发布-订阅模式则不会,这就实现了解耦。

两种设计模式思路是⼀样的,举个⽣活例⼦:

观察者模式:某公司给⾃⼰员⼯发⽉饼发粽⼦,是由公司的⾏政部⻔发送的,这件事不适合交给第三⽅,原因是"公司"和"员⼯"是⼀个整体。

发布-订阅模式:某公司要给其他⼈发各种快递,因为"公司"和"其他⼈"是独⽴的,其唯⼀的桥梁是"快递",所以这件事适合交给第三⽅快递公司解决。

上述过程中,如果公司⾃⼰去管理快递的配送,那公司就会变成⼀个快递公司,业务繁杂难以管理,影响公司⾃身的主营业务,因此使⽤何种模式需要考虑什么情况两者是需要耦合的。

实现

发布-订阅模式在前端开发中被广泛应用,主要是因为它能够简化组件之间的通信、解耦业务逻辑、实现事件驱动等。

以下是前端中发布-订阅模式的一些常见应用场景:

  • DOM事件处理:在处理 DOM 事件时,发布-订阅模式也非常有用。你可以通过订阅 DOM 元素上的特定事件来触发相应的操作,而不用在每个事件处理函数中手动绑定事件。
  • 事件总线:发布-订阅模式可以用来实现事件总线,方便组件之间的事件通信。例如,Vue.js 中的 EventBus,可以用来在组件之间发布和订阅事件。
  • 全局状态管理:在大型应用中,需要管理全局状态的变化并通知相关组件更新,发布-订阅模式可以作为全局状态管理的一种解决方案。例如,Redux 和 MobX 等状态管理库中就使用了发布-订阅模式。
  • 消息通知:当需要实现消息通知功能时,发布-订阅模式也能发挥作用。例如,实时通知、消息提醒等功能可以通过发布-订阅模式来实现。
  • ......

发布-订阅模式在前端开发中有许多实际应用,可以帮助我们更好地管理和组织组件之间的通信,实现解耦并提高代码的可读性和可维护性。

DOM事件处理

在 DOM 上绑定的事件处理函数 addEventListener就是使用的发布-订阅模式。

在前端开发中,发布-订阅模式可以很好地用于处理 DOM 事件,使得事件的订阅和处理更加灵活和分离。下面是一个简单的发布-订阅模式 DOM 事件处理的示例:

js 复制代码
const eventHandler = {
  events: {}, // 存储事件及对应的处理函数
​
  subscribe(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
  },
​
  publish(event, data) {
    if (!this.events[event]) return;
    this.events[event].forEach(callback => callback(data));
  }
};
​
// 订阅按钮点击事件
eventHandler.subscribe('buttonClick', function() {
  console.log('Button clicked!');
});
​
// 模拟按钮触发点击事件
document.getElementById('button').addEventListener('click', function() {
  eventHandler.publish('buttonClick');
});

在这个示例中,我们首先创建了一个 eventHandler 对象,该对象有两个方法:subscribe 订阅事件和添加处理函数,publish 触发事件并执行对应的处理函数。

然后,我们订阅了一个名为 buttonClick 的事件,并将处理函数打印出相应消息。最后,我们在页面上的按钮上绑定了点击事件,当按钮点击时,会触发 buttonClick 事件并执行对应的处理函数。

这样的设计使得我们能够更加灵活地管理和组织 DOM 事件的处理逻辑,将事件和处理逻辑进行分离,提高了代码的可读性和可维护性。

事件总线

在 Vue.js 中,发布-订阅模式可以通过一个事件总线来实现,方便组件之间的事件通信。这个事件总线可以使用 Vue 实例作为中介来实现发布和订阅事件。

下面是一个简单的发布-订阅模式 Vue 事件总线的示例:

假设有两个组件 ComponentAComponentB,它们之间需要进行事件的订阅和发布。我们可以通过一个全局的 Vue 实例作为事件总线来实现。

首先,创建一个包含事件总线的 eventBus.js 文件:

js 复制代码
// eventBus.js
​
import Vue from 'vue';
​
export const eventBus = new Vue();

然后在 ComponentA 组件中订阅事件:

jsx 复制代码
<template>
  <div>
    <p>Component A</p>
  </div>
</template>
​
<script>
import { eventBus } from './eventBus.js';
​
export default {
  mounted() {
    eventBus.$on('customEvent', (data) => {
      console.log('Event received in Component A:', data);
    });
  }
}
</script>

ComponentB 组件中发布事件:

jsx 复制代码
<template>
  <div>
    <p>Component B</p>
    <button @click="handleButtonClick">Click me to send event</button>
  </div>
</template>
​
<script>
import { eventBus } from './eventBus.js';
​
export default {
  methods: {
    handleButtonClick() {
      eventBus.$emit('customEvent', { message: 'Hello from Component B' });
    }
  }
}
</script>

在这个示例中,我们使用了 eventBus.js 中创建的事件总线,在 ComponentA 中订阅了一个名为 customEvent 的事件,并定义了处理函数来打印收到的事件消息。在 ComponentB 中,当按钮点击时触发了 customEvent 事件,并传递了消息数据。

通过这个方式,我们实现了不同组件之间的事件订阅和发布,使得它们可以在不直接通信的情况下进行交互。这种方式适用于一些简单的组件通信场景,对于复杂的应用建议使用 Vuex 等专门的状态管理工具来处理全局状态。

优缺点

发布-订阅模式最大的优点就是解耦

发布-订阅模式优点:

  1. 解耦:发布-订阅模式可以使发布者和订阅者之间解耦,它们互不依赖于彼此的具体实现,从而降低组件之间的耦合度。
  2. 异步:发布-订阅模式支持异步操作,发布者可以在任何时间发布事件,订阅者也可以在适当的时间订阅事件。这种异步机制非常灵活,适用于需要非阻塞操作的场景。
  3. 动态:发布-订阅模式支持动态添加新的订阅者和取消订阅者,使得系统更加灵活和可扩展。
  4. 可复用性:通过发布-订阅模式可以实现事件的广播,可以让多个订阅者同时接收相同的事件消息,提高了代码的可复用性。

发布-订阅模式缺点:

  1. 内存泄漏:发布-订阅模式在使用过程中需要手动管理订阅和取消订阅,如果不及时取消订阅可能会导致内存泄漏。
  2. 调试困难:因为事件的发布和订阅是分布式的,可能会导致事件流难以追踪,使得调试和代码理解变得困难。
  3. 性能问题:在一些多次触发事件的场景下,发布-订阅模式可能会存在性能问题,因为事件的广播需要循环遍历所有订阅者。
  4. 难以维护:在一个复杂的系统中,如果过度使用发布-订阅模式可能会导致事件流变得难以维护和理解,增加代码的复杂性。

参考文献

观察者模式/发布-订阅模式

相关推荐
前端Hardy3 分钟前
HTML&CSS: 实现可爱的冰墩墩
前端·javascript·css·html·css3
编程、小哥哥6 分钟前
设计模式之抽象工厂模式(替换Redis双集群升级,代理类抽象场景)
redis·设计模式·抽象工厂模式
李老头探索12 分钟前
Java面试之Java中实现多线程有几种方法
java·开发语言·面试
web Rookie33 分钟前
JS类型检测大全:从零基础到高级应用
开发语言·前端·javascript
Au_ust41 分钟前
css:基础
前端·css
帅帅哥的兜兜41 分钟前
css基础:底部固定,导航栏浮动在顶部
前端·css·css3
yi碗汤园44 分钟前
【一文了解】C#基础-集合
开发语言·前端·unity·c#
就是个名称1 小时前
购物车-多元素组合动画css
前端·css
编程一生1 小时前
回调数据丢了?
运维·服务器·前端
丶21361 小时前
【鉴权】深入了解 Cookie:Web 开发中的客户端存储小数据
前端·安全·web