依赖管理是开发中常见的问题,这里对依赖的管理的各种方案做记录与分析,供之后的开发参考
- 为了方便执行相应的
ts脚本,需要安装ts-node - 由于用到了
Reflect Metadata特性,需要安装依赖npm install reflect-metadata - 在
tsconfig.json中配置emitDecoratorMetadata项为true
ioc 为 Inversion of Control 的缩写(控制反转),是面向对象编程中的一种设计原则:
- 在面向对象编程中不可避免的会遇到对象与对象间相互依赖的问题(比如A对象调用B对象中的方法)
- 若让对象自身去管理这些依赖则不可避免的会造成代码高度耦合,且难以维护和调试
- 解决的策略为统一管理相应对象的实例,并按照各对象的依赖将被依赖的对象注入到目标对象,方便目标对象的调用
目前在 js 中的 ioc 框架或是使用到 ioc 策略的框架有:
inversifyJs
injection
nestjs
上述经典的 ioc 框架中:
- 会在初始时对依赖的
service做分析,按照相应的依赖链,逐一的去实例化相应的service,并注入到目标对象 - 好处是充分利用了
typescript的Reflect MetaData特性,使得相应注入与标记轻量方便,且在对IDE的类型提示有比较好的支持 - 缺陷是对于实际开发场景中达到一定复杂度后的循环依赖问题没有很好的解决(出现循环依赖时会在依赖分析的步骤中抛错)
nestjs中使用其自己构建的ioc结构对相应的service以及controller进行管理- 且对于循环依赖问题,提供了
forwardRef的策略解决 - 由于使用
Reflect Metadata特性使得其不需要做额外工作便对IDE的类型提示很友好
eggjs 对于依赖采用了暴力且直接的解决方案:
- 即将所有的
service与controller按文件路径规则放在全局对象中 - 每一个
service和controller引用这个全局对象实现不同service的相互调用以及controller对service的调用 - 使用
egg-ts-helper生成对应service与controller的类型声明,以便于给IDE提供友好的类型提示 - 由于使用挂载到全局对象的方式,结构上天然解决了循环依赖问题
这里有一个简单的还原 eggjs 思路的案例: egg依赖解决案例
这里综合参考 nestjs 与 eggjs 做了一个简易版的 ioc 作为学习的参考,代码案例参考 自制简易ioc代码参考,在 example 目录执行 ts-node run.ts 即可
在理解 ioc 之前需要对以下知识做了解
参考 decorator 目录下的实际案例,使用 ts-node 即可执行验证
metadata(元数据): 主要是描述数据属性的数据(比如存储位置,历史数据,类型等)
Reflect Metadata 是 ES7 的一个提案,它主要用来在声明的时候添加和读取元数据。使用该特性可以方便的给 Class 以及 Class 中的属性添加描述,以下是该特性的使用案例,可以使用 ts-node 执行验证
基础使用案例 base.ts
自实现相应的decorator custom.ts
被修饰后增加的默认metadata信息 default.ts
注入的简单案例 inject.ts
参考链接 Reflect Metadata