核心是分层推进继承重构:底层保兼容、中层建规范、顶层靠工具链兜底;第一步冻结伪继承并抽离纯函数,第二步用寄生组合式继承稳住旧代码,第三步双轨运行渐进替代,第四步工程化收口降级负担。
核心不是“一刀切替换”,而是分层推进:底层保兼容、中层建规范、顶层靠工具链兜底。目标是让新功能自然长在现代结构上,存量代码有明确退出路径,团队不因迁移停摆。
第一步:冻结新增伪继承,划定可组合边界
立即禁止所有为复用工具逻辑而写的 extends BaseUtils、extends MixinHelper 类。这类继承不表达语义关系,只暴露耦合风险。
- 把原“父类”中的纯函数方法(如 formatTime、deepClone)全部抽成独立模块,统一用 import { formatTime } from '@/utils' 调用
- 对仍需共享状态的逻辑(如 loading 管理、错误重试),封装为 Hook 或轻量类(如 useLoading()、new RetryPolicy(config)),注入到业务类实例中
- 所有新组件、服务、控制器,必须用 class A extends B 编写,且 B 必须是真正满足 is-a 关系的领域基类(如 ReportService extends BaseService)
第二步:用寄生组合式继承稳住旧代码底线
针对仍需支持 IE9–10 或老旧 WebView 的模块,不强推 class,而是采用经过验证的寄生组合式继承模式:
- 定义空构造函数 function F() {} 作原型中转
- F.prototype = Parent.prototype,再令 Child.prototype = new F()
- 手动修复 constructor 指向,全程不执行 Parent 构造函数,避免副作用
- 该模式由 ESLint 规则 + 自定义 codemod 脚本自动识别并加固,不依赖人工判断
第三步:构建双轨运行与渐进替代机制
允许新旧继承方式并存,但通过配置和工厂实现行为解耦:
- 定义能力接口(如 interface AuthProvider),编写多个实现(LegacyAuth / ModernAuth)
- 启动时读取环境配置,调用 createAuthProvider(env.auth.mode) 创建实例,注入到主类
- 为每个被废弃的旧基类提供同名组合式替代包(如 @/legacy/BaseController → @/composition/ControllerCore),API 尽量对齐,降低迁移成本
- 所有新功能必须对接组合式包;存量模块按业务节奏逐步切换,不设 deadline,但每季度评审替换率
第四步:工程化收口,让兼容性不侵入业务代码
把兼容负担交给构建链,而非开发者大脑:
- 使用 @babel/preset-env 按目标浏览器自动降级 class 和 super,无需手写 ES5 继承片段
- 关键基类(如全局 EventBus、Store)提供轻量 polyfill,仅在缺失环境动态注入
- ESLint 启用 no-proto、no-class-assign 等规则,TypeScript 开启 strictBindCallApply,从编码阶段拦截误用











