博客 / 詳情

返回

理解 Angular 依賴注入

之前只知道依賴注入是Angular中的一個特性,對於依賴注入有一個大概的瞭解,但是並沒有仔細查詢過依賴注入,這裏記錄一下對依賴注入的重新學習。

什麼是依賴注入:

當你開發系統的某個較小部件時(例如模塊或類),你可能需要使用來自其他類的特性。例如,你可能需要 HTTP 服務來進行後端調用。

依賴注入或 DI 是一種設計模式和機制,用於創建應用程序的某些部分並將其傳遞到需要它們的應用程序的其他部分。Angular 支持這種設計模式,你可以在應用程序中使用它來提高靈活性和模塊化程度。

從上面可以得出,依賴注入涉及到依賴提供者與依賴使用者,在依賴提供者註冊之後就可以在系統的其他地方使用依賴了。
在Angular中,提供的依賴通常是服務,當然也可以是其他內容。在註冊後,依賴會被注入到注入器內,然後就可以在系統的其他地方使用了,注入依賴項的最常見方法是在類的構造函數中聲明它。當 Angular 創建組件、指令或管道類的新實例時,它會通過查看構造函數的參數類型來確定該類需要哪些服務或其他依賴項。

注入器是在應用程序啓動期間自動創建的,根注入器只有根模塊中一個,然後每個模塊中另外有屬於自己的注入器。(當然注入器也不只存在於模塊)

image.png

image.png

使用中的性質

只可以在被Angular管理的類中使用

Angular DI中,允許帶有 Angular 裝飾器的類(例如組件、指令、管道和可注入對象)配置它們所需的依賴項。

也就是允許被Angular管理的類配置他們所需要的依賴項,而在其他的工具類中,則無法使用Angular的DI。

當有人請求依賴項時,注入器會檢查其註冊表以查看那裏是否已有可用的實例。如果沒有,就會創建一個新實例並將其存儲在註冊表中。

下面結合代碼簡單演示:

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

@Injectable()
export class AppService {
  title = 'AppService';
}
constructor(appService: AppService) {
  console.log(appService.title);
}

首先定義了一個AppService,並且在根模塊的AppComponent組件中請求注入一個AppService實例,此時根注入器發現註冊表中沒有可用實例,就嘗試使用AppService類型來創建一個新實例,但是發現在根模塊中並沒有提供應該如何去實例化的類,所以就會報錯。此時將AppService添加到根模塊的providers中即可。

結果報錯:
image.png

作用範圍

首先需要明確模塊中定義的依賴提供者(providers)可以在本模塊注入,也可以在其子模塊中注入。

在 NgModule 級別,要使用 @NgModule 裝飾器的 providers 字段。在這種情況下,AppService 可用於此 NgModule 或與本模塊位於同一個 ModuleInjector 的其它模塊中聲明的所有組件、指令和管道。

比如,模塊關係如圖:
image.png
此時在B模塊中注入了一個AppService,就可以在AppModule以及AModule中使用,AppModule和A、B兩個模塊的關係:在AppModule中import A和B兩個模塊。

對比引用內容也就是,AppService可以用於此NgModule(BModule),或與本模塊位於同一個 ModuleInjector(RootModuleInjector) 的其他模塊(AModule)中聲明的所有組件(AComponent, BComponent),指令和管道。

可以理解成在根模塊中引入了A和B兩個模塊,那麼A和B兩個模塊中的providers都可以在根模塊中使用,也就是根模塊的providers = AProviders ∪ BProviders。所以在子模塊中(AModule)也就可以使用根模塊的依賴提供者。

搖樹優化

在 @Injectable 元數據中註冊提供者還允許 Angular 通過從已編譯的應用程序中刪除沒用到的服務來優化應用程序,這個過程稱為搖樹優化(tree-shaking)。

這裏的元數據是@Injectable中提供的數據,註冊提供者之後,如果某個依賴提供者沒有被使用,那麼注入器會將這些依賴提供者刪除來進行優化。

依賴注入配置

依賴注入需要為providers賦值,如providers: [AppService],可以出現在模塊,服務,組件等Angular 裝飾器中。
常見的方式是:
providers: [AppService],這其實是一種簡寫,擴展之後就是:
providers: [{provide: AppService, useClass: AppService}],當提供者令牌為服務類時,注入器的默認行為是使用 new 運算符實例化該類。
{provide: AppService, useClass: AppService}是Provide接口的一個實現,有關該接口的內容:
其中第一個字段provide:屬性包含一個令牌,該令牌會作為定位依賴值和配置注入器時的鍵。(值為"提供者令牌",可以是服務類,也可以是InjectionToken)
第二個字段的值叫做提供者定義對象,用來告訴注入器如何創建依賴值;對於他的鍵,可以不只是useClass,可選項有:

  • useClass
  • useExisting
  • useFactory
  • useValue

useClass(類提供者)

這個提供者鍵名能讓你創建並返回指定類的新實例。
用法:

providers: [{provide: AppService, useClass: AppService1}]
providers: [{provide: AppService, useClass: AppService2}]

useExisting(別名提供者)

useExisting 提供者鍵允許你將一個令牌映射到另一個。
即對一個實例提供兩種訪問方式。
比如:

providers: [
    AppService
    {provide: IndexService, useExisting: AppService}
]

這樣在注入依賴的時候可以使用

constructor(service: AppService) {
}


constructor(service: IndexService) {
}

這兩種方式,最終訪問的都是一個實例。

useFactory(工廠提供者)

useFactory 提供者鍵允許你通過調用工廠函數來創建依賴對象。

使用這種方法,你可以根據 DI 和應用程序中其他地方的可用信息創建動態值。

可見官方示例,最終代碼:

providers: [
  Logger,
  UserService,
  { provide: HeroService,
    useFactory: heroServiceFactory,
    deps: [Logger, UserService]
  }
]

DI也就是指其中的deps的內容,heroServiceFactory是一個(logger: Logger, userService: UserService) => HeroService,其通過userService中的內容來動態的實例化HeroService。

useValue(值提供者)

useValue 鍵允許你將固定值與某個 DI 令牌相關聯

InjectionToken 對象

可以定義和使用一個 InjectionToken 對象來為非類的依賴(函數、對象、基本類型[例如字符串或 Boolean]或任何其他類型)選擇一個提供者令牌。

// 定義提供者令牌
export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');

// 依賴提供
providers: [{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }]

// 依賴使用
constructor(@Inject(APP_CONFIG) config: AppConfig) {
  this.title = config.title;
}

new InjectionToken<AppConfig>('app.config')中的AppConfig表示依賴提供者的類型,'app.config'是對提供者令牌的描述,兩者用來指明此令牌的用途。
這裏的AppConfig是一個接口,Ts是可以使用接口作為提供者令牌的,但是由於程序在運行時使用的是js,所以沒有可供 DI 框架使用的運行時表示或令牌。所以下面這種寫法是不可取的

[{ provide: AppConfig, useValue: HERO_DI_CONFIG })]

constructor(private config: AppConfig){ }
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.