依赖管理是开发中常见的问题,这里对依赖的管理的各种方案做记录与分析,供之后的开发参考
- 为了方便执行相应的
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