Angular 的依赖注入系统
实际上,任何懒加载模块都可以从根应用注入器和依赖模块注入器中获取所需的服务与令牌。这一机制的核心在于 CombinedInjector——每当实例化一个带有依赖的特性模块时,Angular 都会自动创建这个组合注入器。

当被懒加载模块覆盖的 CMS 组件被实例化时,它能够访问的服务来自两个层级:
- 模块注入器层级——从特性模块注入器开始,逐级向上经过依赖模块注入器和根注入器。
- 元素注入器层级——在每个 DOM 元素处隐式创建的注入器链条。
上述内容更偏概念性描述。要想真正深入理解,必须从 Angular 的依赖注入体系、懒加载机制以及 Combined Injector 这几个核心知识点入手。
Angular 的依赖注入(DI)本质上是一种管理代码间依赖关系的设计模式——它让代码更加整洁、可读性更高、更易于维护和测试。整个 DI 系统由三个关键部分组成:注入器、提供者和令牌。
- 注入器:负责在需要时创建依赖对象并交付给请求方。Angular 提供两种注入器:ModuleInjector(模块层级)和 ElementInjector(组件层级)。
- 提供者:告知注入器如何创建或获取依赖,可以是类、工厂函数、值或别名。
- 令牌:注入器用于定位提供者的键,通常直接使用类名作为令牌。
懒加载模块
在大型应用中,我们显然不希望一开始就加载所有功能模块——按需加载才是最佳实践。Angular 允许将特性模块设计为懒加载模块,这样只有当用户首次导航到对应路由时,模块才会被加载和实例化。这样做最直接的优势:初始加载速度显著提升,因为只拉取真正需要的内容。
Combined Injector
每次实例化一个带有依赖的懒加载模块时,Angular 都会创建一个 CombinedInjector。它实际上是 ModuleInjector 和 ElementInjector 的组合体,使得懒加载模块能够无缝访问根应用注入器和依赖模块注入器中的服务。
Lazy Loaded Module 中的 Combined Injector 技术
在 Angular 的模块化架构中,Lazy Loaded Module(延迟加载模块)是一项令人印象深刻的功能。每个模块都拥有自己的依赖注入器,用于管理该模块所需的服务和依赖关系。而懒加载模块的一大亮点在于可以在运行时动态加载,从而有效优化应用性能。
下面我们来详细剖析 Lazy Loaded Module 中的 Combined Injector(组合注入器)技术——它让模块之间能够共享服务和令牌,理解这一机制对于提升 Angular 应用效率至关重要。
组合注入器是什么?
依赖注入是 Angular 管理组件与服务之间依赖关系的核心模式。每个模块都有自己的注入器来管理依赖项,但 Lazy Loaded Module 引入了一个新概念:Combined Injector。
这个特殊的注入器能够合并来自两个来源的服务和令牌:
- 根应用注入器
- 依赖模块的注入器
换句话说,一个 Lazy Loaded Module 不仅能够访问自身模块的依赖,还能获取根应用级别和依赖模块级别的服务。这种能力让模块在需要时可以共享通用服务,避免重复创建实例,从而提升性能和内存利用率。
Combined Injector 的工作原理
核心流程十分简单:每当一个带有依赖项的特性模块被实例化时,Angular 就会创建一个全新的 Combined Injector。这个新注入器会从不同来源收集服务和令牌。
具体而言,当懒加载模块被加载时,Angular 会执行以下步骤:
- 创建一个 Combined Injector。
- 将根应用注入器中的服务和令牌复制过来。
- 把依赖模块注入器中的服务和令牌也复制过来。
- 模块内的组件需要服务时,直接从 Combined Injector 中获取。
这样一来,懒加载模块里的组件无需手动创建额外实例,就能轻松访问根应用和依赖模块的服务。
模块注入器层次和元素注入器层次
前面提到,懒加载模块中的组件可以从两个源获取服务:模块注入器层次和元素注入器层次。下面分别详细说明。
模块注入器层次
模块注入器层次是从特性模块注入器开始的查找链条。特性模块是 Lazy Loaded Module 的主模块,它可以包含其他依赖模块。查找顺序为:从特性模块注入器出发,逐级向上,直到根应用注入器。
举例来说:假设有一个 Lazy Loaded Module,其中包含特性模块 A 和依赖模块 B。特性模块 A 中的组件可以从 A 的注入器、B 的注入器以及根应用注入器中获取服务。这种层级结构确保了组件能够访问整个应用的服务,不受自身模块的限制。
元素注入器层次
元素注入器层次是在 DOM 元素级别创建的注入器链条。每当 Angular 组件被实例化并插入 DOM 时,都会生成一个元素注入器。与模块注入器层次不同,它的生命周期与 DOM 元素绑定。
在这个层次中,服务的查找基于 DOM 元素的层级关系。组件可以访问它所在 DOM 元素及其父元素中的服务。
再举一个例子:假设 Lazy Loaded Module 中的一个组件嵌套在多个父元素中。元素注入器层次允许该组件根据它在 DOM 树中的位置访问不同的服务,而无需关心模块的层级结构。
实际应用和最佳实践
理解了 Combined Injector 和注入器层次结构之后,在实际项目中该如何运用它们?
1. 服务的共享
如果多个 Lazy Loaded Module 都需要访问同一类服务,建议将这些服务提升到根模块中,通过 Combined Injector 共享。这样就不会产生多个实例,性能自然更优。
2. 避免不必要的依赖
在 Lazy Loaded Module 中,添加依赖模块要谨慎。每个依赖都会让 Combined Injector 变得更复杂、更庞大。只有在确实需要时才引入,保持应用轻量高效。
3. 使用元素注入器
对于需要与 DOM 元素交互的组件,利用元素注入器会非常方便。它让组件能轻松访问与其 DOM 位置相关的服务,而不必依赖模块的结构。
结论
Lazy Loaded Module 中的 Combined Injector 技术是 Angular 中一个非常实用的功能——它让模块之间可以共享服务和令牌,进而提升应用的性能与可维护性。理解 Combined Injector 的工作方式以及模块注入器层次、元素注入器层次的概念,能够帮助我们更好地设计和优化 Angular 应用。
在实际开发中,合理运用 Lazy Loaded Module 和 Combined Injector,既能构建出高效可扩展的应用,又能保持代码的整洁和可维护性。
