发布订阅模式详解

发布订阅模式(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)。
  • 调试难度:消息流通过中间层,可能增加追踪事件来源的复杂度。
相关推荐
苏渡苇5 小时前
优雅应对异常,从“try-catch堆砌”到“设计驱动”
java·后端·设计模式·学习方法·责任链模式
短剑重铸之日6 小时前
《设计模式》第十一篇:总结
java·后端·设计模式·总结
feasibility.7 小时前
AI 编程助手进阶指南:从 Claude Code 到 OpenCode 的工程化经验总结
人工智能·经验分享·设计模式·自动化·agi·skills·opencode
BD_Marathon7 小时前
七大设计原则介绍
设计模式
YigAin9 小时前
Unity23种设计模式之 享元模式
设计模式·享元模式
范纹杉想快点毕业1 天前
实战级ZYNQ中断状态机FIFO设计
java·开发语言·驱动开发·设计模式·架构·mfc
茂桑1 天前
DDD领域驱动设计-基础设施层
设计模式·架构
小温冲冲1 天前
通俗且全面精讲工厂设计模式
设计模式
进击的小头1 天前
设计模式与C语言高级特性的结合
c语言·设计模式
小温冲冲1 天前
通俗且全面精讲单例设计模式
开发语言·javascript·设计模式