原型链继承解决能力复用而非解耦;解耦靠接口抽象、边界隔离与依赖控制。大型单页应用中,应以契约定义行为边界,用工厂函数创建服务实例,业务模块仅通过构造函数接收服务,视图层只做数据映射与事件转发。

原型链继承本身不是解耦手段,它解决的是对象间能力复用问题;真正支撑模块解耦的,是接口抽象、边界隔离与依赖控制。在大型单页应用中,把“原型链”当作底层机制来用,把“解耦”交给架构设计来管——这才是最优范式。
不靠 extends,靠契约定义行为边界
避免让业务模块直接继承框架组件或跨域服务类(如 class OrderService extends VueComponent)。这类写法会把框架生命周期、响应式逻辑硬编码进业务层。
取而代之的是:用 TypeScript 接口或纯 JS 对象形状(shape)声明能力契约:
-
NavigationService:只含
goTo(path)、replace(path)、back() -
DataQueryService:只含
fetch<t>(key, params)</t>、invalidate(key) -
UserService:只含
getCurrent(): User | null、onLogin(cb: () => void)
所有具体实现(Vue Router 适配器、Axios 封装、Auth0 客户端)都放在 adapters/ 目录下,业务模块 import 的永远是接口,不是实现。
用工厂函数统一创建服务实例,切断 new 调用链
搜索代码中所有 new ApiClient()、new OrderValidator()、Config.getInstance() ——这些是耦合源头,不是原型链的问题,而是控制权失控的表现。
集中收口到一个启动函数:
- 在
main.ts或app.ts中调用createServices() - 该函数返回一个包含全部服务的对象:
{ api, nav, user, cache } - 所有业务类(如
OrderProcessor)只通过构造函数接收这些服务,不 import 框架模块、不 new 任何东西 - 测试时直接传入 mock 对象,无需启动 Vue/React 环境
原型链仅用于横向能力组合,不用于跨域继承
允许用原型链复用通用能力,但必须限定在同层、同域、无副作用的模块内。例如:
- 多个报表组件共享
exportToExcel()、printPreview()方法 → 抽成ReportFeatureMixin,通过applyMixins(ReportA, [ReportFeatureMixin])注入 - 表单校验规则集合(邮箱、手机号、密码强度)→ 封装为
ValidationRules对象,供各表单类通过Object.assign(this, ValidationRules)或原型挂载复用
禁止跨模块、跨技术栈使用原型链(如让 TMS 模块的类继承 ERP 模块的类),这会制造隐式依赖和版本锁定风险。
视图层彻底交出控制权,只做数据映射与事件转发
组件内部不出现以下任何内容:
-
if (user.role === 'admin') { ... }——状态判断上提到 ViewModel 或 Coordinator -
this.$router.push(...)或history.pushState()——由注入的NavigationService执行 -
localStorage.getItem('theme')——由StorageService统一管理 -
new Date().toISOString()这类时间逻辑 ——封装进ClockService,便于测试时冻结时间
组件只接收扁平化的 view state(如 { loading: false, items: [], error: null }),并触发语义化事件(submit、filterChange),其余一概不管。










