发布-订阅模式应用场景及示例说明
发布-订阅模式应用场景及示例说明
发布-订阅模式通过事件中心解耦组件间的直接依赖,适用于需要灵活、动态通信的场景。以下是典型应用场景及具体示例:
1. 用户界面(UI)组件通信
场景:多个独立组件需要响应同一事件(如用户登录、数据更新)。
示例:
- 用户登录成功后的全局更新
登录组件发布事件,其他组件(如导航栏、购物车、个人中心)订阅该事件并更新状态。// 登录成功后发布事件 eventEmitter.emit("userLogin", { username: "Alice" });// 导航栏组件订阅 eventEmitter.on("userLogin", (user) => {updateNavBar(user); });// 购物车组件订阅 eventEmitter.on("userLogin", (user) => {loadCart(user.id); });
2. 模块解耦
场景:跨模块通信,避免直接依赖。
示例:
- 数据层与视图层解耦
数据模块在数据变化时发布事件,视图模块订阅事件并重新渲染,无需直接调用渲染方法。// 数据模块(发布者) function updateData(newData) {dataCache = newData;eventEmitter.emit("dataChanged", newData); }// 视图模块(订阅者) eventEmitter.on("dataChanged", (data) => {renderView(data); });
3. 异步操作协调
场景:多个异步任务完成后触发后续操作。
示例:
- 并行异步任务全部完成后通知
多个 API 请求并行执行,所有请求完成后触发事件。let completedTasks = 0; const totalTasks = 3;// 每个异步任务完成后触发事件 fetchUser().then(() => {eventEmitter.emit("taskComplete"); }); fetchOrders().then(() => {eventEmitter.emit("taskComplete"); }); fetchProducts().then(() => {eventEmitter.emit("taskComplete"); });// 监听所有任务完成 eventEmitter.on("taskComplete", () => {completedTasks++;if (completedTasks === totalTasks) {console.log("所有任务完成");} });
4. 插件系统或扩展机制
场景:允许第三方插件监听核心系统的事件,扩展功能。
示例:
- 编辑器插件机制
编辑器核心发布事件(如内容修改、保存),插件订阅事件实现自定义功能(如自动保存、语法检查)。// 编辑器核心(发布者) class Editor {insertText(text) {// 插入文本逻辑eventEmitter.emit("contentChanged", text);} }// 自动保存插件(订阅者) eventEmitter.on("contentChanged", (text) => {autoSaveToLocal(text); });// 语法检查插件(订阅者) eventEmitter.on("contentChanged", (text) => {checkGrammar(text); });
5. 微服务架构中的事件驱动
场景:服务间通过事件通信,避免直接 API 调用。
示例:
- 订单创建后通知其他服务
订单服务发布订单创建事件,库存服务、支付服务、日志服务订阅事件并执行操作。// 订单服务(发布者) orderService.createOrder(orderData).then(() => {eventEmitter.emit("orderCreated", orderData); });// 库存服务(订阅者) eventEmitter.on("orderCreated", (order) => {inventoryService.updateStock(order.items); });// 支付服务(订阅者) eventEmitter.on("orderCreated", (order) => {paymentService.processPayment(order.total); });
6. 游戏开发中的事件驱动逻辑
场景:游戏对象的状态变化触发多系统响应。
示例:
- 玩家得分变化更新 UI 和成就系统
得分管理器发布得分变化事件,UI 组件和成就系统订阅事件。// 得分管理器(发布者) class ScoreManager {addScore(points) {this.score += points;eventEmitter.emit("scoreUpdated", this.score);} }// UI 组件(订阅者) eventEmitter.on("scoreUpdated", (score) => {updateScoreDisplay(score); });// 成就系统(订阅者) eventEmitter.on("scoreUpdated", (score) => {if (score >= 1000) unlockAchievement("High Score"); });
7. 浏览器事件机制
场景:DOM 事件(如点击、滚动)本质上是发布-订阅模式。
示例:
- 按钮点击触发多个回调
点击按钮后执行多个独立操作(如提交表单、记录日志、关闭弹窗)。// 发布者:浏览器内置的事件触发机制 document.getElementById("btn").addEventListener("click", () => {// 隐式发布 "click" 事件 });// 订阅者 1:表单提交 document.getElementById("btn").addEventListener("click", submitForm);// 订阅者 2:日志记录 document.getElementById("btn").addEventListener("click", logClick);
总结
适用场景共性:
- 松耦合需求:组件/模块间需通信但避免直接依赖。
- 动态扩展性:需灵活添加或移除功能响应。
- 一对多或多对多通信:一个事件触发多个独立操作。
不适用场景:
- 简单的一对一通信(直接调用更高效)。
- 需要严格顺序控制的操作(事件触发顺序不可控)。
通过发布-订阅模式,可以显著提升代码的可维护性和扩展性,尤其适合复杂系统中的事件驱动架构设计。