发布订阅(pub/sub)的核心是中间的事件中心:发布者不用知道有谁在听,订阅者不用知道消息谁发的——靠事件名(主题)连接。前端(EventBus) 适用场景:跨组件通知(非父子)、兄弟组件联动、通知栏。浏览器自身机制就是发布订阅:addEventListener 订阅、dispatch 触发;Node 的 EventEmitter 同理。
与观察者模式的区别常被问:观察者模式是「目标直接维护观察者列表」( Subject.addObserver),发布订阅多了一个事件中心做转发,发布者与订阅者彻底解耦。手写实现也是经典面试题:on 订阅、emit 触发、off 取消。实战注意:组件销毁一定要 off,否则旧组件还在收事件 = 内存泄漏。
// 手写一个事件总线(经典手写题) 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'); // 不再有输出