发布订阅模式与观察者模式详解
发布订阅模式与观察者模式详解
在前端架构与设计模式相关的面试与实践中,“发布订阅模式(Publish/Subscribe)”与“观察者模式(Observer)”经常被放在一起讨论。很多人会觉得两者“差不多”,但在实现方式和应用场景上,它们还是有本质区别的。
本文主要回答三个问题:
- 观察者模式与发布订阅模式分别是什么?
- 两者的差异到底在哪?为什么很多资料会混着说?
- 在前端工程中,如何实现和使用它们?
一、观察者模式(Observer Pattern)
1. 概念
观察者模式定义了一种“一对多”的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生变化时,会自动通知所有观察者。
关键点:
- 主体(Subject) 持有 观察者(Observer)列表
- 观察者直接 订阅主体,主体状态变更时 主动回调观察者
- 主体与观察者之间存在直接引用关系
2. 经典结构
1 | |
3. 简单实现示例(以 JS 为例)
1 | |
在这个例子中:
Subject直接持有Observer实例的引用- 通知时直接调用
observer.update
二、发布订阅模式(Publish/Subscribe Pattern)
1. 概念
发布订阅模式通过“事件通道 / 事件中心”让发布者与订阅者解耦:发布者不会直接感知订阅者的存在,而是将消息发布到某个“频道(topic/event)”,由事件中心负责把消息转发给所有订阅该频道的订阅者。
关键点:
- 多了一个独立的“事件中心(Event Bus / Broker)”
- 发布者 只关心“往哪个频道发消息”,而不关心谁在监听
- 订阅者 只关心“订阅哪个频道”,而不关心是谁发布
- 发布者和订阅者之间完全解耦
2. 典型结构
1 | |
3. 简易事件总线实现
1 | |
三、观察者模式 vs 发布订阅模式:核心区别
从结构和耦合度上来看,可以这样总结:
1. 是否存在“中介”
- 观察者模式:
- 主体(Subject)直接维护观察者列表
- 主题与观察者存在直接依赖
- 发布订阅模式:
- 引入第三方“事件中心 / 消息中介”(Event Bus / Broker)
- 发布者与订阅者 不直接依赖 彼此,只依赖事件中心
2. 通知机制
- 观察者模式:
- 主体状态变化时,调用
observer.update - 通常绑定的是“对象间关系”(如一个 Model 被多个 View 观察)
- 主体状态变化时,调用
- 发布订阅模式:
- 发布者往某个 topic 发布数据
- 由事件中心调度调用订阅回调
- 通常绑定的是“事件 / 主题”的概念
3. 使用体验上的差异(伪代码对比)
观察者模式:
1 | |
发布订阅模式:
1 | |
简单一句话概括:
观察者:被观察者知道“我有这些观察者”;
发布订阅:发布者不知道“有哪些订阅者”,只知道“我发到哪个主题”。
四、前端中的常见应用场景
1. 观察者模式:响应式、数据驱动视图
典型例子:
- Vue 2 的响应式系统(
Object.defineProperty+ 依赖收集) - 老版本 MVVM 框架中的“数据模型与视图”的绑定
在 Vue 2 中:
Dep管理着一组 watcher(观察者)- 当数据变更时,
Dep会通知对应 watcher 更新视图
这本质上就是“观察者模式”的一个具体实现。
2. 发布订阅模式:事件总线(Event Bus)
在前端应用中,经常会使用“事件总线”做跨组件通信:
Vue 2 中的 Event Bus(简化写法)
1 | |
这里:
- 各组件不直接互相依赖
- 通过 EventBus 这个“事件中心”来解耦
React 中也有类似用法
可以自己实现一个简单的 eventBus,在多个组件中共享,用于:
- 不适合用 Redux/Context 管理的“局部事件”
- 跨层级弹窗通知、全局提示等
五、如何选择:什么时候用观察者,什么时候用发布订阅?
可以从几个维度考虑:
1. 对象关系的明确程度
- 观察者模式 更适合:
- 某个“主体对象”与“观察者对象”关系明确且稳定,如数据模型与视图
- 发布订阅模式 更适合:
- 多模块、跨层级之间的“松散通信”
- 不希望某个模块知道所有“接收方”的存在
2. 解耦需求
- 如果你希望 A 与 B 完全解耦,尤其是“许多不同模块都可能关心某类事件”,用发布订阅更好。
- 如果是“一对多”的强逻辑绑定(一个模型驱动多个视图),用观察者更贴切。
3. 系统复杂度
- 发布订阅引入了“中间层”,需要维护事件中心,也更容易出现:
- 事件名称拼写错误不好排查
- 订阅后忘记取消,导致内存泄漏
- 观察者模式则更直接,但会在主体与观察者之间形成显式依赖。
六、工程实践建议
- 为事件命名制定统一规范(如
模块:动作→user:login、cart:updated)。 - 对于发布订阅模式:
- 订阅时最好保留取消订阅的句柄,在组件卸载/模块销毁时及时清理。
- 避免在全局随意创建多个事件中心,最好统一从一个模块导出。
- 对于观察者模式:
- 用于数据与视图之间的绑定时,注意避免过度嵌套依赖,导致调试困难。
- 从长远看,当系统规模变大时,可以考虑:
- 使用更结构化的状态管理/事件流方案(Redux、Vuex/Pinia、RxJS 等),而不仅仅是裸用 EventBus。
七、总结
发布订阅模式与观察者模式既相似又不同:
- 相同点:本质上都是为了解决“一对多通知”和模块间解耦的问题。
- 不同点:
- 观察者模式:主体直接持有观察者列表,主动调用观察者方法;
- 发布订阅模式:通过事件中心转发消息,发布者与订阅者互不直接感知。
在前端工程中:
- 响应式系统、数据驱动视图通常更接近“观察者模式”;
- 事件总线、跨模块事件广播更偏向“发布订阅模式”。
理解二者的差异,并结合具体业务场景选择合适的实现,有助于写出更清晰、可维护性更高的前端架构。
发布订阅模式与观察者模式详解
https://sunjc.vip/2026/02/28/发布订阅模式与观察者模式详解/