设计模式
是一套被反复使用、多数人知晓、经过分类编目的、代码设计经验的总结。它是为了可重用代码,让代码更容易的被他人理解并保证代码的可靠性。设计模式合集链接:
概述
发布-订阅
是⼀种消息范式
,消息的发送者(称为发布者)不会将消息直接
发送给特定的接收者(称为订阅者)。⽽是将发布的消息分为不同的类别,⽆需了解哪些订阅者(如果有的话),同样的,订阅者可以表达对⼀个或多个类别的兴趣,只接收感兴趣的消息,⽆需了解哪些发布者存在。
在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 事件总线的示例:
假设有两个组件 ComponentA
和 ComponentB
,它们之间需要进行事件的订阅和发布。我们可以通过一个全局的 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 等专门的状态管理工具来处理全局状态。
优缺点
发布-订阅模式最大的优点就是解耦
发布-订阅模式优点:
解耦
:发布-订阅模式可以使发布者和订阅者之间解耦,它们互不依赖于彼此的具体实现,从而降低组件之间的耦合度。异步
:发布-订阅模式支持异步操作,发布者可以在任何时间发布事件,订阅者也可以在适当的时间订阅事件。这种异步机制非常灵活,适用于需要非阻塞操作的场景。动态
:发布-订阅模式支持动态添加新的订阅者和取消订阅者,使得系统更加灵活和可扩展。可复用性
:通过发布-订阅模式可以实现事件的广播,可以让多个订阅者同时接收相同的事件消息,提高了代码的可复用性。
发布-订阅模式缺点:
内存泄漏
:发布-订阅模式在使用过程中需要手动管理订阅和取消订阅,如果不及时取消订阅可能会导致内存泄漏。调试困难
:因为事件的发布和订阅是分布式的,可能会导致事件流难以追踪,使得调试和代码理解变得困难。性能问题
:在一些多次触发事件的场景下,发布-订阅模式可能会存在性能问题,因为事件的广播需要循环遍历所有订阅者。难以维护
:在一个复杂的系统中,如果过度使用发布-订阅模式可能会导致事件流变得难以维护和理解,增加代码的复杂性。