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(private theme: string) { (AppTheme) |
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(private config: AppConfig) { (APP_CONFIG) |
这样在Component中,就可以使用this.config
来访问这个配置对象了。