发布订阅模式通过事件总线实现松耦合通信,核心方法为on、emit、off;适用于跨组件通信、异步解耦、插件扩展等场景,并需注意通配符、once、错误隔离及类型安全。

JavaScript 中的发布订阅模式(Pub/Sub)是一种松耦合的通信机制,核心是通过一个中间对象(事件总线)来管理事件的注册(订阅)与触发(发布),让发送方(发布者)和接收方(订阅者)互不感知。它不依赖具体对象引用,适合组件解耦、跨模块通信等场景。
手写一个轻量级事件总线
只需一个对象维护事件名与回调函数列表的映射,提供 on(订阅)、emit(发布)、off(取消订阅)三个基础方法:
示例代码:
class EventBus {
constructor() {
this.events = {};
}
on(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
}
emit(event, ...args) {
const callbacks = this.events[event];
if (callbacks && callbacks.length) {
callbacks.forEach(cb => cb(...args));
}
}
off(event, callback) {
const callbacks = this.events[event];
if (callbacks) {
this.events[event] = callbacks.filter(cb => cb !== callback);
}
}
}
// 使用
const bus = new EventBus();
bus.on('user-login', (user) => console.log('欢迎', user.name));
bus.emit('user-login', { name: '张三' });
事件总线在应用中的典型作用
它主要解决模块间低侵入式通信问题,尤其在没有全局状态管理或框架支持的环境中非常实用:
立即学习“Java免费学习笔记(深入)”;
- 跨组件通信:比如侧边栏点击菜单后,通知主内容区切换视图,无需父子传参或层层透传
- 异步流程解耦:上传完成时触发“刷新列表”、“弹提示”、“记录日志”等多个独立逻辑,彼此不依赖
-
插件系统扩展:主程序只定义关键事件(如
init、destroy),插件自行监听并执行定制逻辑 - 替代部分全局变量或回调地狱:避免硬编码调用,提升可测试性与可维护性
注意事项与增强方向
基础实现够用,但实际项目中常需补充以下能力:
- 支持通配符订阅(如
user.*)或事件命名空间,便于批量管理 - 添加
once方法实现一次性监听,避免重复执行 - 为
off提供按事件名清除全部监听的能力,防止内存泄漏 - 在大型应用中,建议配合 TypeScript 定义事件类型,提高开发体验与安全性
- 注意回调执行顺序与错误隔离:某个订阅者报错不应中断其他回调,可用
try/catch包裹单个调用
与现代框架的对比
Vue 的 $emit/$on(2.x)、React 的 Context + useReducer 或自定义 Hook、以及状态库如 Pinia/Zustand,本质都借鉴了 Pub/Sub 思想。但它们通常更强调响应式更新或状态同步;而纯事件总线更聚焦于“通知”本身,不绑定数据流,适用范围更广,也更适合微前端、SDK、工具类库等场景。











