Angular 為什麼要引入 injection token 的概念
你可以定義和使用一個 InjectionToken 物件來為非類
的依賴選擇一個提供者令牌。
這裡的重點是:非類。
下列例子定義了一個型別為 InjectionToken 的 APP_CONFIG .
import { InjectionToken } from '@angular/core';
export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');
這裡的 APP_CONFIG 只是一個令牌 token,或者說是一個 place holder.
可選的引數
接著,用 APP_CONFIG 這個 InjectionToken 物件在元件中註冊依賴提供者。
providers: [{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }]
語義是,消費者程式碼裡,注入 APP_CONFIG 的令牌,則執行時,令牌會被實際的值 HERO_DI_CONFIG 取代。這個 HERO_DI_CONFIG 不是一個 Angular class, 所以只能以 injection token 的方式註冊提供者。
現在,藉助引數裝飾器 @Inject(),你可以把這個配置物件注入到建構函式中。
constructor(@Inject(APP_CONFIG) config: AppConfig) { this.title = config.title; }
介面和依賴注入
雖然 TypeScript 的 AppConfig 介面可以在類中提供型別支援,但它在依賴注入時卻沒有任何作用。在 TypeScript 中,介面是一項設計期工件,它沒有可供 DI 框架使用的執行時表示形式或令牌。
當轉譯器把 TypeScript 轉換成 JavaScript 時,介面就會消失,因為 JavaScript 沒有介面。
由於 Angular 在執行期沒有介面,所以該介面不能作為令牌,也不能注入它。
因此,下列的程式碼是不合法的:
// Can't use interface as provider token [{ provide: AppConfig, useValue: HERO_DI_CONFIG })]
我們不能把 interface 本身作為一個令牌,因此 Angular 引入了 injection token 的概念。
同樣,下列的程式碼亦不合法,因為 interface 不能作為建構函式的輸入引數型別注入。因此我們需要 @Inject, 將 interface 包裹一層之後再傳入建構函式。
// Can't inject using the interface as the parameter type
constructor(private config: AppConfig){ }