模块应依赖抽象接口而非具体实现,采用命名导出、显式导入、依赖注入、轻量接口契约及事件/状态中间层通信,确保解耦、可测、可替换与低修改扩散风险。

关键在于让模块只依赖抽象接口,不依赖具体实现,同时控制依赖方向和暴露粒度。
用命名导出 + 显式导入替代默认导出
默认导出容易隐藏依赖关系,也限制了模块复用灵活性。命名导出能清晰表达“我提供什么”,导入时也能按需选取,避免无意引入未使用功能。
- ✅ 推荐写法:export const apiClient = {...}; export function validateUser() {...}
- ❌ 避免写法:export default { apiClient, validateUser }(掩盖真实接口、不利于 tree-shaking)
- 导入时只取所需:import { validateUser } from './userValidation.js',不带入无关逻辑
通过依赖注入解耦服务调用
模块内部不直接 require/import 具体服务,而是由外部传入——这样测试时可轻松替换为 mock 实现,修改一个服务也不影响调用方。
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 例如订单处理器不自己引入 userService:function createOrderService(userRepo, paymentGateway) { ... }
- 创建实例时统一注入:const orderSvc = createOrderService(dbUserRepo, stripeClient)
- 模块自身不再持有对其他模块的硬引用,依赖关系外移、可配置、可替换
定义轻量接口契约,而非强依赖具体模块
当多个模块需要协作时,先约定输入输出结构(比如一个 user 对象必须含 id、email、role),而不是让 A 模块直接调用 B 模块的某个方法。
立即学习“Java免费学习笔记(深入)”;
- 用 TypeScript interface 或 JSDoc 注释明确协议:/** @typedef {{ id: string; email: string }} User */
- UI 组件只接收符合该结构的数据,不关心数据来自 API 还是本地缓存
- 后续替换成 GraphQL client 或 IndexedDB 适配器时,只要输出结构一致,组件无需改动
禁止跨域/跨层直连,用事件或状态中间层通信
UI 组件不应直接调用另一个页面组件的方法,也不应直接修改全局 store 或其他模块的私有状态。
- 组件间通信走自定义事件:dispatchEvent(new CustomEvent('user-updated', { detail }))
- 状态变更统一交由 context 或 Zustand/Pinia 等状态库管理,模块只 subscribe 和 commit
- 切断直接调用链,把“谁触发”和“谁响应”解耦,降低修改扩散风险

















