Angular 依赖注入的学习笔记

Angular官方文档

Specifying a provider token

If you specify the service class as the provider token, the default behavior is for the injector to instantiate that class with new.
In the following example, the Logger class provides a Logger instance.

providers: [Logger]

NgModule的providers区域里,指定provider token. 如果将一个服务类直接指定成provider token,默认的行为就是,injector直接用new来初始化那个类。

上面的例子,会触发Logger的构造函数。等价于:

[{ provide: Logger, useClass: Logger }]

这个完整的声明在Angular官方文档里称为expanded provider configuration, 是一个包含两个属性的对象。

  • The provide property holds the token that serves as the key for both locating a dependency value and configuring the injector.

provide属性包含一个token,用于定位一个dependency value和配置injector.

  • The second property is a provider definition object, which tells the injector how to create the dependency value. The provider-definition key can be useClass, as in the example. It can also be useExisting, useValue, or useFactory. Each of these keys provides a different type of dependency, as discussed below.

第二个属性是一个provider定义对象,告诉injector如何创建dependency value. 这个定义可以是useClass,useExisting,useValue,或者useFactory.

一些例子:

[{ provide: Logger, useClass: BetterLogger }]

当使用Logger token时,injector返回BetterLogger的实例。

Aliasing class providers

To alias a class provider, specify the alias and the class provider in the providers array with the useExisting property.

In the following example, the injector injects the singleton instance of NewLogger when the component asks for either the new or the old logger. In this way, OldLogger is an alias for NewLogger.

下列的配置,会导致Component需要使用OldLogger时,会返回NewLogger的singleton实例。当然,如果请求NewLogger,亦会返回NewLogger实例。因此这种情形下,OldLogger是NewLogger语义层面的alias.

[ NewLogger,
  // Alias OldLogger w/ reference to NewLogger
  { provide: OldLogger, useExisting: NewLogger}]

如果使用如下命令行创建 service:

ng generate service User

自动生成的代码:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class UserService {
}

现在,你就可以在应用中到处注入 UserService 了。

该服务本身是 CLI 创建的一个类,并且加上了 @Injectable() 装饰器。默认情况下,该装饰器是用 providedIn 属性进行配置的,它会为该服务创建一个提供者。在这个例子中,providedIn: ‘root’ 指定 Angular 应该在根注入器中提供该服务。

当你把服务提供者添加到应用的根注入器中时,它就在整个应用程序中可用了。 另外,这些服务提供者也同样对整个应用中的类是可用的 —— 只要它们有供查找用的服务令牌。

你应该始终在根注入器中提供这些服务 —— 除非你希望该服务只有在消费方要导入特定的 @NgModule 时才生效。

也可以规定某个服务只有在特定的 @NgModule 中提供。比如,如果你希望只有当消费方导入了你创建的 UserModule 时才让 UserService 在应用中生效,那就可以指定该服务要在该模块中提供:

import { Injectable } from '@angular/core';
import { UserModule } from './user.module';

@Injectable({
  providedIn: UserModule,
})
export class UserService {
}

上面的例子展示的就是在模块中提供服务的首选方式。之所以推荐该方式,是因为当没有人注入它时,该服务就可以被摇树优化掉。如果没办法指定哪个模块该提供这个服务,你也可以在那个模块中为该服务声明一个提供者。

服务是一个广义的概念,它包括应用所需的任何值、函数或特性。狭义的服务是一个明确定义了用途的类。它应该做一些具体的事,并做好。

Angular 把组件和服务区分开,以提高模块性和复用性。 通过把组件中和视图有关的功能与其它类型的处理分离开,你可以让组件类更加精简、高效。

理想情况下,组件的工作只管用户体验,而不用顾及其它。 它应该提供用于数据绑定的属性和方法,以便作为视图(由模板渲染)和应用逻辑(通常包含一些模型的概念)的中介者。

组件应该把诸如从服务器获取数据、验证用户输入或直接往控制台中写日志等工作委托给各种服务。通过把各种处理任务定义到可注入的服务类中,你可以让它被任何组件使用。 通过在不同的环境中注入同一种服务的不同提供者,你还可以让你的应用更具适应性。

当 Angular 创建组件类的新实例时,它会通过查看该组件类的构造函数,来决定该组件依赖哪些服务或其它依赖项。 比如 HeroListComponent 的构造函数中需要 HeroService:

constructor(private service: HeroService) { }

当 Angular 发现某个组件依赖某个服务时,它会首先检查是否该注入器中已经有了那个服务的任何现有实例。如果所请求的服务尚不存在,注入器就会使用以前注册的服务提供者来制作一个,并把它加入注入器中,然后把该服务返回给 Angular。

是否创建新的依赖服务实例,取决于 provider 的具体类型。

当所有请求的服务已解析并返回时,Angular 可以用这些服务实例为参数,调用该组件的构造函数。

HeroService 的注入过程如下所示:从应用的 Injector 里获取 HeroService 实例。

在这里插入图片描述

提供服务

对于要用到的任何服务,你必须至少注册一个提供者。服务可以在自己的元数据中把自己注册为提供者,这样可以让自己随处可用。或者,你也可以为特定的模块或组件注册提供者。要注册提供者,就要在服务的 @Injectable() 装饰器中提供它的元数据,或者在 @NgModule() 或 @Component() 的元数据中。

默认情况下,Angular CLI 的 ng generate service 命令会在 @Injectable() 装饰器中提供元数据来把它注册到根注入器中。本教程就用这种方法注册了 HeroService 的提供者:

@Injectable({
  providedIn: 'root',
})

什么是 Angular 依赖注入的 tree-shaking

When you provide the service at the root level, Angular creates a single, shared instance of HeroService and injects it into any class that asks for it. Registering the provider in the @Injectable() metadata also allows Angular to optimize an app by removing the service from the compiled app if it isn’t used, a process known as tree-shaking.

当你使用特定的 NgModule 注册提供者时,该服务的同一个实例将会对该 NgModule 中的所有组件可用。要想在这一层注册,请用 @NgModule() 装饰器中的 providers 属性:

@NgModule({
  providers: [
   BackendService,
   Logger
 ],
 ...
})

当你在组件级注册提供者时,你会为该组件的每一个新实例提供该服务的一个新实例。 要在组件级注册,就要在 @Component() 元数据的 providers 属性中注册服务提供者。

@Component({
  selector:    'app-hero-list',
  templateUrl: './hero-list.component.html',
  providers:  [ HeroService ]
})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汪子熙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值