但是有几个人取消订阅我,他们很难理解DI及其在Angular中的功能。互联网上没有太多关于如何有效使用DI的资料,对于许多开发人员而言,它归结为使用全局服务或将全局数据从应用程序的根目录传递到组件。
让我们更深入地了解Angular中的这种机制。
你知道你的瘾吗?
有时很难理解您的代码有多少个依赖项。
例如,看一下这个伪类并计算它有多少个依赖项:
import { API_URL } from '../../../env/api-url';
import { Logger } from '../../services/logger';
class PseudoClass {
request() {
fetch(API_URL).then(...);
}
onError(error) {
const logger = new Logger();
logger.log(document.location, error);
}
}
回答
fetch — API, , , .
API_URL — ( ).
new Logger() — , .
document — API .
API_URL — ( ).
new Logger() — , .
document — API .
那怎么了
例如,此类很难测试,因为它依赖于从其他文件导入的数据以及其中的特定实体。
另一种情况:文档和提取将在您的浏览器中无缝运行。但是,如果有一天需要将应用程序转移到服务器端渲染,则必需的全局变量可能不在nodejs环境中。
那么什么是DI,为什么需要它呢?
依赖注入管理应用程序内的依赖。基本上,对于我们来说,对于Angular开发人员来说,这个系统非常简单。有两个主要操作:将一些内容放入依赖关系树或从中获取一些东西。
有关DI的更多理论观点,请阅读控制原理的反转。您还可以观看有关该主题的有趣视频:来自Ilya Klimov的一系列有关IoC和DI的视频(俄语)或一个有关IoC的简短视频(英语)。
所有魔力都来自于我们提供和获取依赖的顺序。
范围如何在DI中工作:
我们可以在DI中放入什么?
DI操作的第一步是放入一些东西。实际上,为此,Angular允许我们在模块,组件或指令的修饰符中编写provider数组。让我们看看这个数组可以包含什么。
提供课程
通常,每个Angular开发人员都知道这一点。这是将服务添加到应用程序中的时间。
当您首次请求Angular时,将创建该类的实例。在Angular 6中,我们根本无法在providers数组中编写类,但可以告诉类本身在DI中应该提供providerIn的位置:
providers: [
{
provide: SomeService,
useClass: SomeService
},
// Angular :
SomeService
]
提供价值
常数值也可以通过DI提供。它可以是带有API URL的简单字符串,也可以是带有数据的复杂Observable。
提供值通常是与InjectionToken结合实现的。该对象是DI引擎的键。首先,您说:“我想获取此密钥的数据。” 后来您来到DI并问:“此钥匙上有东西吗?”
好吧,一个常见的情况是从应用程序的根目录转发全局数据。
最好立即看到它的实际效果,所以让我们来看一个示例的stackblitz:
展开范例
因此,在示例中,我们从DI获得了依赖项,而不是直接从另一个文件中将其作为常量导入。为什么对我们更好?
- 我们可以在DI树中的任何级别覆盖令牌值,而无需更改使用令牌值的组件。
- 测试时,我们可以使用适当的数据模拟令牌的值。
- 该组件是完全隔离的,无论上下文如何,它都将始终相同。
提供工厂
我认为,这是Angular依赖注入引擎中最强大的工具。
您可以创建一个令牌,该令牌将是合并和转换其他令牌的值的结果。
这是另一个stackbitz,其中包含使用流创建工厂的详细示例。
展开范例
在提供工厂节省时间或使代码更具可读性的情况下,您会发现很多情况。有时,我们将依赖项注入到组件中只是为了将它们组合或将它们转换为完全不同的格式。在上一篇文章中,我更详细地研究了此问题,并展示了解决此类情况的另一种方法。
提供现有实例
这不是常见的情况,但是此选项可能是非常有用的工具。
您可以将已经创建的实体放入令牌中。运作良好,与forwardRef。
再看一个带有指令的stackblitz的示例,该指令实现该接口并用useExisting替换另一个标记。在此示例中,我们要为指令挂起的元素的子组件覆盖DI-only令牌值。此外,该指令可以是任何指令-主要是它实现了所需的接口。
展开范例
DI装饰技巧
DI装饰器使您可以使DI查询更加灵活。
如果您不认识所有四个装饰器,建议阅读Medium中的这篇文章。本文使用英语,但是该主题有非常酷和易于理解的可视化。
也没有多少人知道在deps数组中可以使用DI装饰器,该数组为provider工厂准备了参数。
providers: [
{
provide: SOME_TOKEN,
/**
* ,
* [new Decorator(), new Decorator(),..., TOKEN]
* .
*
* ‘null’,
* OPTIONAL_TOKEN
*/
deps: [[new Optional(), OPTIONAL_TOKEN]],
useFactory: someTokenFactory
}
]
代币工厂
InjectionToken构造函数采用两个参数。
第二个参数是具有令牌配置的对象。
令牌工厂是一个在有人首次请求此令牌时调用的函数。在其中,您可以为令牌计算某个标准值,甚至可以通过注入功能访问其他DI实体。
看一下按钮单击流功能的实现示例,但这一次是在令牌工厂。
展开范例
结论
Angular中的DI是一个了不起的话题:乍一看,它没有很多学习方法和工具,但是您可以花几个小时来写和谈论它们给我们带来的可能性和用途。
我希望本文为您提供了基础,您可以在此基础上提出自己的解决方案,以简化在应用程序和库中处理数据的工作。