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

问题背景:

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

传统方式:

网络请求:

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 模块逻辑不会影响其他模块;
-   不需要中转存储,一处触发事件,所有关心该事件的地方都能直接接收数据;
-   遵循 "谁关心这个事件 / 错误,谁就监听处理",实现真正的解耦与分布式处理。
相关推荐
陈随易2 小时前
Postman 被曝自动往 Claude Code 和 Codex 装插件
前端·后端·程序员
Csvn5 小时前
性能监控
前端
Csvn5 小时前
构建工具
前端
FRing036 小时前
vue2干货总结3(computed计算属性、watch监听器)
开发语言·前端·vue.js·vue2
kyriewen8 小时前
Skills 榜前 10 我全装了:只有 3 个能直接用
前端·javascript·程序员
IT_陈寒8 小时前
当Java的hashCode遇上Lombok,我的缓存被全清了
前端·人工智能·后端
工控丫丫9 小时前
帆软看板中经常使用的公式函数及经常搭的组合
前端·javascript·数据库
jerrywus9 小时前
我用 GPT-6.1-sol 做了台会讲解的 3D 售货机:60 罐饮料,一口没喝上
前端·agent·claude
孟陬10 小时前
Foobar 技术周刊#159:当看不懂代码时,如何高效排障调试。脏页陷阱:一条只读查询,写入数 TB 数据。推测解码:大模型加速利器
前端·人工智能·后端
Zhao-Jichao10 小时前
AI Prompting for Everyone 课程笔记 - P17 图像生成 Imaging Generation
前端