发布订阅模式详解

发布订阅模式(Publish-Subscribe Pattern)是一种对象间解耦的通信模式 ,核心思想是:发送者(发布者)不直接与接收者(订阅者)通信,而是通过一个 "中间媒介"(事件总线 / 主题中心)传递消息,双方无需知道对方的存在,从而降低耦合度。

javascript 复制代码
发布者
   │
   │ 发布 "userLogin"事件(带数据)
   ▼
事件总线(Event Bus)
   ├──────────────────────┐
   ▼                      ▼
订阅者A                订阅者B
"userLogin"              "userLogin"
│                         │
└─→ 触发回调(收到数据)   └─→ 触发回调(收到数据)

一、核心角色

  1. 发布者(Publisher):负责产生消息并 "发布" 到中间媒介(无需知道谁会接收)。例:前端的按钮点击事件、后端的订单状态变更。

  2. 订阅者(Subscriber):向中间媒介 "订阅" 感兴趣的消息类型,当有对应消息发布时,会收到通知并处理。例:监听按钮点击的回调函数、接收订单状态变更的短信服务。

  3. 事件总线 / 主题中心(Event Bus/Topic):作为中间枢纽,管理 "消息类型" 与 "订阅者" 的映射关系,接收发布者的消息后,转发给所有订阅该类型的订阅者。

二、与观察者模式的区别

很多人会混淆两者,核心差异在于是否有中间媒介

  • 观察者模式:观察者(Subscriber)直接依赖被观察者(Publisher),被观察者维护观察者列表,状态变化时直接通知。(如:Vue 的响应式系统中,组件直接观察数据变化)
  • 发布订阅模式 :通过独立的 "事件总线" 解耦,发布者和订阅者完全隔离。(如:前端的EventBus、后端的消息队列 Kafka)

三、前端常见应用场景

  1. 组件通信 :非父子组件间通信(如 Vue 的EventBus、React 的PubSub库)。

    javascript 复制代码
    // 简单的EventBus实现
    class EventBus {
      constructor() {
        this.events = {}; // 存储 { 事件类型: [回调函数列表] }
      }
      // 订阅
      on(type, callback) {
        if (!this.events[type]) this.events[type] = [];
        this.events[type].push(callback);
      }
      // 发布
      emit(type, data) {
        if (this.events[type]) {
          this.events[type].forEach(callback => callback(data));
        }
      }
      // 取消订阅
      off(type, callback) {
        if (this.events[type]) {
          this.events[type] = this.events[type].filter(cb => cb !== callback);
        }
      }
    }
    
    // 使用
    const bus = new EventBus();
    // 订阅
    bus.on('userLogin', (user) => console.log('欢迎', user.name));
    // 发布
    bus.emit('userLogin', { name: '张三' }); // 输出:欢迎 张三

事件监听 :DOM 事件本质是发布订阅模式(addEventListener是订阅,用户操作是发布)。

javascript 复制代码
// 订阅点击事件
document.getElementById('btn').addEventListener('click', handleClick);
// 用户点击按钮时,浏览器作为事件总线发布事件,触发handleClick
  1. 状态管理 :Redux/Vuex 中,组件订阅状态变化(subscribe),action 发布状态更新。

  2. 跨页面通信window.postMessage配合发布订阅,实现 iframe 或多标签页通信。

四、优势与注意事项

优势:
  • 解耦:发布者和订阅者互不依赖,便于扩展(新增订阅者无需修改发布者)。
  • 灵活性:支持一对多通信(一个事件可被多个订阅者处理)。
  • 可维护性:集中管理事件,避免代码中散落的直接调用。
注意事项:
  • 内存泄漏 :订阅后需及时取消(如组件销毁时调用off),否则残留的回调会导致内存泄漏。
  • 事件命名规范 :避免事件类型冲突(可使用命名空间,如user:loginorder:pay)。
  • 调试难度:消息流通过中间层,可能增加追踪事件来源的复杂度。
相关推荐
ZouZou老师4 小时前
C++设计模式之命令模式:以家具生产为例
c++·设计模式·命令模式
laocooon5238578865 小时前
C++ 设计模式概述及常用模式
开发语言·c++·设计模式
SadSunset6 小时前
(12)GoF设计模式
设计模式
化作繁星7 小时前
前端设计模式详解
前端·设计模式
ZouZou老师7 小时前
C++设计模式之责任链模式:以家具生产为例
c++·设计模式·责任链模式
赵得C8 小时前
软件设计师前沿考点精讲:新兴技术与性能优化实战
java·开发语言·分布式·算法·设计模式·性能优化
白衣鸽子8 小时前
【基础数据篇】数据等价裁判:Comparer模式
后端·设计模式
Charlo9 小时前
Open-AutoGLM Windows 安装部署教程
算法·设计模式·github
TDengine (老段)9 小时前
TDengine 存储引擎:极速、高压缩、零冗余
android·大数据·数据库·设计模式·时序数据库·tdengine·涛思数据