这些年,总有人跟我说:「雪狼,Angular 的 DI 我懂,不就是 constructor(private service: MyService) 嘛,简单!」
每当这时,我都会笑一笑,告诉他:「兄弟,你看到的,只是冰山一角,是 DI 的『形』。它真正的威力,那掌控应用血脉、让你拥有上帝般创造力的部分,藏在水面之下。那,才是它的『神』。」
今天,我就带你潜入深水,一同探寻 DI 的「神」之领域。
DI 之「神」:非「扁平江湖」,实乃「层级王朝」#
多数人对 DI 最大的误解,就是以为它只是一个扁平的、全局的「服务中心」。大错特错!
Angular 的注入器体系,不是一个混沌的江湖,而是一个等级森严、与你的组件树一一对应的「层级王朝」。

这个王朝的法则很简单,就像古代官员「寻找上级」:当一个组件(地方官员)需要一个依赖(一道批文)时,它先看自己的「衙门」(自身注入器)有没有。没有?那就沿着组件树向上,去「州府」(父组件注入器)找,再没有,就一路找到「京城」(根注入器)。
这个「层级」设计,就是 DI 的「神魂」所在。它赋予了你精细化控制服务实例生命周期的无上权力:
-
根注入器:通过
@Injectable({ providedIn: 'root' })注册的服务,是王朝的「中央军」,全局唯一,贯穿始终。 -
路由注入器:在惰性加载路由中提供的服务,是镇守一方的「节度使」,只在进入其辖区时才上任。
-
组件注入器:在组件
providers数组中提供的服务,是组件的「私家护卫」,每个组件实例都会配备一套全新的卫队。
DI 之「法」:四道「圣旨」与 inject() 的「王炸」#
providers 数组可不是简单的名单,它是皇帝颁发不同依赖的四种「圣旨」,每一种都有其妙用。
-
useClass(偷天换日):{ provide: Logger, useClass: BetterLogger }。这道圣旨说:「凡有索要Logger者,赐BetterLogger!」 这是实现「面向接口编程」的无上法门。 -
useValue(开箱即食):{ provide: APP_CONFIG, useValue: {...} }。这道圣旨最直接:「把这个朕亲手打包好的APP_CONFIG对象,直接发下去!」 -
useFactory+inject()(炼丹术):这是最强的「王炸」组合。useFactory相当于一道命令:「宣『炼丹宗师』上殿,为朕现场炼制此物!」 而现代的inject()函数,则让这位宗师拥有了在炼丹过程中,随时从天地间(当前注入上下文)汲取灵气(其他依赖)的通天神力。// 现代炼丹术 { provide: ApiService, useFactory: () => { const http = inject(HttpClient); // 现场吸取「HttpClient」灵气 const config = inject(APP_CONFIG); // 现场吸取「APP_CONFIG」灵气 // ... 可加入任意复杂的判断逻辑,炼制不同神丹 ... return new ApiService(http, config.apiEndpoint); } // deps 数组这个「拐杖」已经不再需要了! } -
useExisting(赐马甲):{ provide: OldLogger, useExisting: NewLogger }。这道圣旨用于安抚前朝老臣:「还有用旧令牌OldLogger的,别给他另起炉灶了,让他去隔壁NewLogger家领俸禄吧。」 这是重构时的「维稳」神技。
DI 之「术」:更优雅的「寻访」方式#
在「王朝」中寻找上级,还有一些「微操」,比如「只能问同级(@Self)」、「可以问不到(@Optional)」等。在 inject() 时代,这些「寻访」规则的表达也更优雅了。
过去,像一堆挂饰,叮叮当当
class SomeClass {
constructor(@Optional() @Self() private logger?: Logger) {
}
}现在,像一份清晰的公文
class SomeClass {
private logger = inject(Logger, {
optional: true, // 相当于 @Optional()
self: true, // 相当于 @Self()
});
}将寻访规则作为 inject 函数的第二个参数,意图更集中,代码更清爽。
结语:形而上者谓之道#
《易经》有云:
形而上者谓之道,形而下者谓之器。
(超越具体形态的规律、思想,称之为「道」;具备具体形态的工具、事物,称之为「器」。)
如果说,你过去只是把 DI 当做一个方便获取服务实例的「器」,那么希望今天之后,你能悟到它背后那关于「层级」、「秩序」、「创造」的「道」。
在独立组件的时代,DI 的重要性不降反升。它不再仅仅是后勤,它就是连接万千「独立王国」的「丝绸之路」与「神经中枢」。当你开始思考「这个服务应该由路由提供,还是组件自己提供?」、「我能否用 useFactory 动态替换掉某个依赖?」时,恭喜你,你已经不再是一个只会用剑的「兵」,而开始拥有「铸剑师」的智慧了。