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

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

设计模式合集链接:

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

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

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

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

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

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

概述

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

在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. 难以维护:在一个复杂的系统中,如果过度使用发布-订阅模式可能会导致事件流变得难以维护和理解,增加代码的复杂性。

参考文献

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

相关推荐
余生H7 分钟前
前端的全栈混合之路Meteor篇:关于前后端分离及与各框架的对比
前端·javascript·node.js·全栈
程序员-珍10 分钟前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发
axihaihai14 分钟前
网站开发的发展(后端路由/前后端分离/前端路由)
前端
MinBadGuy26 分钟前
【GeekBand】C++设计模式笔记5_Observer_观察者模式
c++·设计模式
流烟默26 分钟前
Vue中watch监听属性的一些应用总结
前端·javascript·vue.js·watch
2401_8572979137 分钟前
招联金融2025校招内推
java·前端·算法·金融·求职招聘
刷帅耍帅40 分钟前
设计模式-生成器模式/建造者模式Builder
设计模式·建造者模式
茶卡盐佑星_1 小时前
meta标签作用/SEO优化
前端·javascript·html
Ink1 小时前
从底层看 path.resolve 实现
前端·node.js
金灰1 小时前
HTML5--裸体回顾
java·开发语言·前端·javascript·html·html5