面试题库 词条目录
前端Wiki/词条/发布订阅模式

发布订阅模式

定义解耦通信的设计模式:发布者发消息到事件中心,订阅者按主题接收,互不感知

发布订阅(pub/sub)的核心是中间的事件中心:发布者不用知道有谁在听,订阅者不用知道消息谁发的——靠事件名(主题)连接。前端(EventBus) 适用场景:跨组件通知(非父子)、兄弟组件联动、通知栏。浏览器自身机制就是发布订阅:addEventListener 订阅、dispatch 触发;Node 的 EventEmitter 同理。

与观察者模式的区别常被问:观察者模式是「目标直接维护观察者列表」( Subject.addObserver),发布订阅多了一个事件中心做转发,发布者与订阅者彻底解耦。手写实现也是经典面试题:on 订阅、emit 触发、off 取消。实战注意:组件销毁一定要 off,否则旧组件还在收事件 = 内存泄漏。

JavaScript
// 手写一个事件总线(经典手写题)
class EventBus {
  constructor() { this.events = {}; }

  on(name, fn) {                          // 订阅
    (this.events[name] ||= []).push(fn);
    return this;
  }

  once(name, fn) {                        // 订阅一次
    const wrap = (...a) => { fn(...a); this.off(name, wrap); };
    this.on(name, wrap);
  }

  emit(name, ...args) {                   // 发布
    (this.events[name] || []).slice().forEach(fn => fn(...args));
  }

  off(name, fn) {                         // 注销
    if (!fn) { delete this.events[name]; return; }
    const list = this.events[name];
    if (list) this.events[name] = list.filter(f => f !== fn);
  }
}

// 使用
const bus = new EventBus();
const handler = msg => console.log('收到:', msg);
bus.on('login', handler);
bus.emit('login', 'Tom');        // 收到: Tom
bus.off('login', handler);       // ★ 组件销毁时必须注销
bus.emit('login', 'Tom');        // 不再有输出
⚠ 常见误区
🔗 相关词条事件委托Pinia 状态管理Context 跨层通信
← 返回来源章节手写发布订阅与事件总线