天天看点

详谈Angular依赖注入(一)

作者:乂舟zz

Angular提供了一个强大的依赖注入(Dependency Injection,简称DI)系统,使得组件之间的依赖关系变得清晰,可管理,从而提高了代码的可读性和可维护性。今天就来谈谈Angular依赖注入系统。

一、什么是依赖注入?

依赖注入是一种设计模式,它处理了对象之间的依赖关系。在没有依赖注入的情况下,一个对象需要自己创建或者查找它所需要的其他对象。然而,这种方式会导致对象之间的耦合度增加,当一个对象改变时,可能会影响到其他的对象,从而增加了代码的复杂度。

相比之下,依赖注入通过将依赖对象的创建和查找工作交给依赖注入系统,使得对象之间的耦合度大大减小。这样,每个对象只需要关注自己的职责,而无需关心它的依赖对象如何创建和查找,从而提高了代码的可读性和可维护性。

二、依赖注入原理

Angular通过使用装饰器(Decorator)和元数据(Metadata)来实现依赖注入。

首先来看看装饰器。在Angular中,装饰器是一种特殊的声明,它可以修改类、方法、属性或参数的行为。Angular使用装饰器来标记和修改类,使得这些类可以被依赖注入系统管理。例如,@Injectable()装饰器告诉Angular这个类可以被依赖注入系统管理。

‬@Injectable({

providedIn: 'root'

})

export class MyService {

// ...

}

其次来看看元数据。元数据是关于类、方法、属性或参数的数据。Angular使用元数据来了解如何创建和管理类。例如,providers元数据告诉Angular如何创建类的实例。

‬@NgModule({

providers: [

MyService

]

})

export class AppModule { }

在这个例子中,providers元数据告诉Angular,当需要MyService的实例时,应该新建一个MyService的实例。

现在我们已经理解了Angular使用装饰器和元数据来实现依赖注入,接下来我们来看看Angular的依赖注入系统是如何工作的。

首先,当Angular需要一个类的实例时,它会首先检查这个类是否有@Injectable()装饰器。如果有,那么Angular就会通过调用这个类的构造函数来创建一个新的实例。在调用构造函数时,Angular会检查构造函数的参数。对于每一个参数,Angular会尝试从依赖注入系统中获取一个匹配的实例。

下面这个示例,展示了如何在TypeScript中使用Angular的依赖注入系统:

‬import 'reflect-metadata';

import { ReflectiveInjector, Injectable, Injector } from 'injection-js';

class Http {}

@Injectable()

class Service {

constructor(private http: Http) {}

}

@Injectable()

class Service2 {

constructor(private injector: Injector) {}

getService(): void {

console.log(this.injector.get(Service) instanceof Service);

}

createChildInjector(): void {

const childInjector = ReflectiveInjector.resolveAndCreate([Service], this.injector);

}

}

const injector = ReflectiveInjector.resolveAndCreate([Service, Http]);

console.log(injector.get(Service) instanceof Service);

在这个示例中,Service类依赖于Http类。当Angular需要一个Service的实例时,它会通过调用Service的构造函数来创建一个新的实例。在调用构造函数时,Angular会尝试从依赖注入系统中获取一个Http的实例。如果成功,那么Angular就会将这个Http的实例作为参数传递给Service的构造函数。

Angular的依赖注入系统还支持多级注入。这意味着一个类可以依赖于其他类,而这些类又可以依赖于其他的类,以此类推。在这种情况下,Angular会递归地处理这些依赖关系,直到所有的依赖都被解决。

除此之外,Angular的依赖注入系统还提供了一些高级的特性,比如提供器(providers)、注入令牌(InjectionToken)、可选的依赖项(@Optional())、自我注入(@Self())等等。它们可以让我们更灵活、更高效地使用Angular的DI系统,使得依赖注入系统更加灵活和强大。

三、提供器(Providers)

提供器是告诉Angular如何创建依赖项的说明。最简单的提供器就是类提供器,它告诉Angular可以使用new关键字来创建依赖项。例如,当我们在@Injectable()装饰器中设置providedIn: 'root'时,就是告诉Angular要为根注入器创建一个该服务的实例。这实际上就是使用了一个类提供器,如下所示:

‬{ provide: MyService, useClass: MyService }

此外,Angular的DI系统还支持其他几种类型的提供器,如值提供器、工厂提供器和别名提供器,它们可以让我们更灵活地创建和配置依赖项,我会在后续文章中做详细介绍。

四、 注入令牌(InjectionToken)

有时,我们可能需要注入一些不是类的值,例如配置对象、基础类型的值等。为了能够让Angular的DI系统识别这些值,我们可以使用InjectionToken来创建一个注入令牌。

‬import { InjectionToken } from '@angular/core';

export const MY_CONFIG_TOKEN = new InjectionToken<string>('My Config');

然后,我们可以在提供器中使用这个令牌来提供一个值:

‬{ provide: MY_CONFIG_TOKEN, useValue: 'My Config Value' }

五、可选的依赖项(@Optional())

在默认情况下,如果Angular的DI系统找不到一个依赖项,就会抛出一个错误。但有时,我们可能希望在找不到依赖项时,也能让应用正常运行。这时,就可以使用@Optional()装饰器来标记这个依赖项是可选的。

‬constructor(@Optional() private myService: MyService) { }

六、自我注入(@Self())

默认情况下,Angular的DI系统会在当前注入器和其所有父注入器中查找依赖项。如果我们希望Angular只在当前注入器中查找依赖项,就可以使用@Self()装饰器。

‬‬constructor(@Self() private myService: MyService) { }

七、跳过自身(@SkipSelf())

相反,如果我们希望Angular跳过当前注入器,只在其父注入器中查找依赖项,就可以使用@SkipSelf()装饰器。

‬constructor(@SkipSelf() private myService: MyService) { }

以上就是Angular依赖注入系统的基本介绍,我们可以更好地控制依赖项的创建和注入,从而编写出更灵活、更可维护的代码。后续文章会对细节做详细介绍,欢迎大家持续关注。#前端框架#​#前端面试#​#暑期创作大赛#​

详谈Angular依赖注入(一)

继续阅读