前端项目开发/维护中降低成本的方式之一:降低耦合度

问题背景:

复制代码
随着项目功能不断迭代,公共方法或通用组件内部的判断逻辑会越来越多,导致代码臃肿、后期维护成本急剧上升。
典型场景:网络请求返回统一结果(如登录失效、参数错误),但不同页面需要执行不同的业务逻辑。如果将所有页面逻辑都耦合在公共方法中,会让公共模块变得庞大且难以维护。

传统方式:

网络请求:

javascript 复制代码
service.interceptors.response.use(
    async (res) => {
        if(res.headers['set-cookie']){
            cookieHeader = res.headers['set-cookie']
        }
        return res.data;
    },
    (err) => {
        return Promise.reject(err);
    }
);

业务功能:

markdown 复制代码
-   A 页面调用接口,把结果存到 **Pinia/Vuex** 或 **本地存储**
-   B 页面 **监听状态变化**,才能做处理

现在的解决方法:

复制代码
采用【事件总线作为中间件】的设计方案,通过发布订阅模式解耦模块。让各业务页面在自身模块内独立监听、处理事件,既不侵入公共逻辑,也不互相影响,从而显著降低系统耦合度,提升代码可维护性。

代码实现:

定义中间件:

typescript 复制代码
/**
 * 事件总线
 *  作用:
 *      1、监听多个事件,并执行相应的操作
 *      2、降低组件之间的依赖关系,避免后续模块的修改导致其他模块的修改
 */

// 定义事件名称,可以添加多个
const eventNames = ["API:UN_AUTH", "API:VALIDATE_ERROR"] as const

type EventName = (typeof eventNames[number])
class EventEmitter {
    private listeners: Record<EventName, Set<Function>> = {
        "API:UN_AUTH": new Set(),
        "API:VALIDATE_ERROR": new Set(),
    };

    /**
     * 监听事件
     * @param eventName 固定事件名
     * @param listener 监听器函数
     */
    on(eventName: EventName, listener: Function) {
        this.listeners[eventName].add(listener);
    }

    /**
     * 触发事件
     * @param eventName 固定事件名
     * @param args 传递的参数
     */
    emit(eventName: EventName, ...args: any[]) {
        this.listeners[eventName].forEach((listener) => listener(...args));
    }

    /**
     * 移除单个监听器,在页面销毁时执行,避免内存泄露
     * @param eventName 事件名
     * @param listener 待移除的监听器
     */
    off(eventName: EventName, listener: Function) {
        this.listeners[eventName].delete(listener);
    }

    /**
     * 清空某事件的所有监听器可在退出项目时执行
     * @param eventName 事件名
     */
    clear(eventName: EventName) {
        this.listeners[eventName].clear();
    }
}

export default new EventEmitter()
```

网络请求返回结果处:【以下代码只是模拟使用】

php 复制代码
```
if(response.code === 401){
  eventEmitter.emit('API:UN_AUTH', { message: '未授权'})
}
else if(response.code === 404){ 
  eventEmitter.emit('API:VALIDATE_ERROR', { message: '校验失败'})
}
```

页面/组件逻辑处理:

javascript 复制代码
eventEmitter.on("API:UN_AUTH", (res) => {
  console.log("页面的总线",  res)
})

传统方案和事件总线方案的区别:

1、传统方案

markdown 复制代码
-   耦合度较高,一处修改可能牵连其他模块;
-   网络请求返回的数据,必须在**调用处处理**,如需在其他位置使用,必须先存入状态管理或本地存储;
-   遵循 "谁调用接口,谁处理错误" 的模式,错误只回传到调用方。

2、事件总线方案

diff 复制代码
-   耦合度更低,修改 A 模块逻辑不会影响其他模块;
-   不需要中转存储,一处触发事件,所有关心该事件的地方都能直接接收数据;
-   遵循 "谁关心这个事件 / 错误,谁就监听处理",实现真正的解耦与分布式处理。
相关推荐
deli00711 分钟前
1 个汉字算几个 token?实测中英文 prompt 与代码的编码差异
前端
无限聚核17 分钟前
抖音 certificate.prepare 之后怎么验券?和美团 msconsume 差在哪,无限聚核
前端·微信小程序
2603_9658966227 分钟前
Grid 网格布局|CSS 二维页面排版体系
前端·css
有味道的男人42 分钟前
搜好货平台关键词商品搜索接口:分页容错与商品数据轻量化提取方案
服务器·前端·javascript
lovingsoft1 小时前
Chrome Lighthouse 详解
前端·chrome
Qlly2 小时前
全栈工程师怎么接需求?从一句「加个功能」聊到能开发、能验收
前端·后端
马可家的菠萝2 小时前
你的微信“文件传输助手”,是不是已经成了第二个网盘?
前端·后端·架构
略略略啦2 小时前
Vibe Coding 基于Spec DD和TDD的实践指南
前端
shmily麻瓜小菜鸡2 小时前
前端实现图片与PDF文字识别敏感信息打码方案
前端·pdf·状态模式
打工仔折腾 AI2 小时前
LangChain 工具调用实战:从 Tool 定义到 Agent 执行链路拆解
java·前端·人工智能·后端·python·langchain·ai agent 实战