发布订阅模式详解

发布订阅模式(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:login、order:pay)。
  • 调试难度:消息流通过中间层,可能增加追踪事件来源的复杂度。
相关推荐
sarasuki9 小时前
为什么 Agent 工具需要一个「协议」而不是「框架」:MCP 成为标准的底层逻辑
设计模式·agent·mcp
vivo互联网技术9 小时前
知识不是文件,也不是向量 | KDC 系列 02
人工智能·设计模式·架构
Zane19946 天前
一个 new 就能创建对象,为什么还要拆出单例、工厂、建造者、原型四种模式?
设计模式
怕浪猫7 天前
一行命令复刻爆款视频,我把 Hypit 从安装跑到了出片
人工智能·设计模式·程序员
Zane19947 天前
函数式编程里的函数,其实不是你天天写的那个函数——三大编程范式的边界在哪
设计模式
Zane19948 天前
策略模式现在该不该上?一次讲清楚过度设计和设计不足怎么找平衡
设计模式
她说..8 天前
常见设计模式-模板方法模式
java·spring·设计模式·springboot
xiaofeiyang1509 天前
第六章 · 桥接 — 三支毛笔,画出九种颜色
设计模式
Shadow(⊙o⊙)9 天前
OTOL设计模式 One Thread One Loop
服务器·网络·设计模式
sarasuki10 天前
如何让LLM 能在半夜偷偷打开网易云呢?
人工智能·设计模式·agent