There are many ways to define a dependency provider in Angular. In this article, we will discuss the following ways:
useClass
useClass是依赖注入中提供provider最常见的方式,我们项目中90%用的都是这种方式,下面来看看怎么使用这种方式,假设我们有一个ProductComponent组件,它依赖一个OrderService服务,我们可以像下面这样提供OrderService:
1 | ({ |
上面的例子其实是简写形式,它等价于下面的完整形式:也就是当provide对应的值和useClass对应的值相同时,我们可以采用这种简写形式。
1 | providers: [{ provide: OrderService, useClass: OrderService }], |
还有一点需要注意的是:如果OrderService是providedIn: 'root'的话,那么我们就不需要在providers中再次提供OrderService了,直接在构造函数中注入即可。
1 | export class ProductComponent { |
useClass在单元测试中特别有用,如果要测试的Service很复杂,我们可以提供一个mockService来代替他,这个mockService只要满足最小测试范围内即可,这样就可以保证测试的独立性。
1 | providers: [{ provide: UserService, useClass: mockUserService}] |
useExisting
useExisting通常用于提供一个别名,也就是说,useExisting提供了多种访问同一Service的方式,下面来看一个例子:
假设我们的Order分为线下订单和线上订单,那么线上订单我们可以单独定义一个Service,叫做OnlineOrderService,这个Service需要的功能OrderService中都有,所以我们可以如下使用它。
1 | ({ |
这里:OnlineOrderService是OrderService的别名,我们可以通过OnlineOrderService来访问OrderService的实例。
这种方式通常发生在旧系统到新系统的迁移时,比如迁移的时候,我们可能会更改一些Service的名字,但是为了兼容旧系统,我们可以通过useExisting来提供别名。
以Logger为例,假设旧系统使用的是OldLoggerService, 新系统使用的是NewLoggerService,那么我们可以这样定义:
1 | providers: [{provide: OldLoggerService, useExisting: NewLoggerService}] |
useFactory
useFactory,顾名思义,这种方式使用一个工厂方法来提供依赖的实例,通常用在需要参数的Service中,上面的例子中,我们提供的Service都是无参数的,下面我们来看一个需要参数的Service。
下面这个HeroService需要一个Logger和一个isAuthorized参数,无论是useClass,还是useExisting都无法满足这个需求,这时我们就可以使用useFactory来提供这个Service。
1 | class HeroService { |
useFactory后面跟的是一个函数,所以我们先定义这个函数。(注意Factory函数是一个injection context,在这个函数内部可以使用inject()方法来注入其他依赖。)
1 | const heroServiceFactory = (logger: Logger, userService: UserService) => |
然后使用useFactory来提供HeroService。
1 | providers: [ |
注意观察,上面的工厂函数heroServiceFactory中的参数logger和userService是通过deps传递进来的。
useValue
useValue允许你绑定一个静态值到一个token上,这种方式通常用于提供一些常量,比如配置信息等。如果我们需要运行时动态取值,就可以使用这种方式了。
useValue的使用步骤如下:
- 定义一个token
- 使用
useValue来提供这个token - 在构造函数中注入这个token
token是简单值
定义一个token
首先新建一个文件src/app/config/AppConfig.ts用来存储token。这里我们创建一个token用来存储主题信息。
1 | import {InjectionToken} from "@angular/core"; |
使用useValue来提供这个token
由于我们这个token比较简单,就是字符串类型的,所以直接用字符串’light’来提供这个token。
1 | providers: [{provide: AppTheme, useValue: 'light'}], |
在构造函数中注入这个token
1 | constructor((AppTheme) private theme: string) { |
token是对象
对于复杂的token,我们将其封装到一个JS对象中,如果一个app中有多个配置项,我们可以将这些配置项封装到一个对象AppConfig中,然后使用useValue来提供这个对象。
定义一个token
首先定义token对应的对象类型,这里使用typescript中的interface来定义。
1 | // src/app/config/AppConfig.ts |
然后定义该类型对应的token实例。
1 | // src/app/config/AppConfig.ts |
使用useValue来提供这个token
先创建一个AppConfig对象,然后使用useValue来提供这个对象。
1 | // app.component.ts |
在构造函数中注入这个token
1 | constructor((APP_CONFIG) private config: AppConfig) { |
这样在Component中,就可以使用this.config来访问这个配置对象了。