Angular 2 Provider

依賴注入(DI) 是 Angular 2 的核心,在深刻了解DI的工做原理以前,咱們必須先搞清楚 Provider 的概念。html

圖片描述

在 Angular 2 中咱們使用 Provider 來描述與 Token 關聯的依賴對象的建立方式。Angular 2 中依賴對象的建立方式有四種,它們分別是:json

  • useClasssegmentfault

  • useValueapi

  • useExisting數組

  • useFactoryide

useClass

@Injectable()
export class ApiService {
   constructor(
      public http: Http, 
      public loadingCtrl: LoadingController) {
   }
   ...
}

@NgModule({
  ...
  providers: [
       { provide: ApiService, useClass: ApiService } // 可以使用簡潔的語法,即直接使用ApiService
  ]
})
export class CoreModule { }

useValue

{ provide: 'API_URL', useValue: 'http://my.api.com/v1' }

useExisting

{ provide: 'ApiServiceAlias', useExisting: ApiService }

useFactory

export function configFactory(config: AppConfig) {
  return () => config.load();
}

@NgModule({
  ...
  providers: [
       { provide: APP_INITIALIZER, useFactory: configFactory, 
        deps: [AppConfig], multi: true }
  ]
})
export class CoreModule { }

使用 Provider 的正確姿式

1.建立 Token函數

Token 的做用是用來標識依賴對象,Token值多是 Type、InjectionToken、OpaqueToken 類的實例或字符串。一般不推薦使用字符串,由於若是使用字符串存在命名衝突的可能性比較高。在 Angular 4.x 之前的版本咱們通常使用 OpaqueToken 來建立 Token,而在 Angular 4.x 以上的版本版本,推薦使用 InjectionToken 來建立 Token 。詳細的內容能夠參考, 如何解決 Angular 2 中 Provider 命名衝突this

2.根據實際需求選擇依賴對象的建立方式,如 useClass 、useValue、useExisting、useFactoryspa

3.在 NgModule 或 Component 中註冊 providers3d

4.使用構造注入的方式,注入與 Token 關聯的依賴對象

/**
* 封裝Http服務,如在每一個Http的請求頭中添加token,相似於Ng1.x中的攔截器
*/
@Injectable() 
export class ApiService {
   constructor(
   // 注入Angular 2 中的Http服務,與Ng1.x的區別:
   // 在Ng1.x中調用Http服務後,返回Promise對象
   // 在Ng2.x中調用Http服務後,返回Observable對象
      public http: Http) { 
   }
   ...
}

/**
* AppModule
*/
@NgModule({
  ...
  providers: [
       { provide: ApiService, useClass: ApiService } // 可以使用簡潔的語法,即直接使用ApiService
  ]
})
export class AppModule { }

/**
* 系統首頁
*/
@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  constructor(
    public apiService: ApiService // 使用構造注入的方式,注入ApiService的實例對象
  ) { }
  
  ngOnInit(): void {
    this.apiService.get(HOME_URL) // 獲取首頁相關的數據
    .map(res => res.json()) // 返回的res對象是Response類型的實例
    .subscribe(result => {
      ...
    })
  }
}

我有話說

1.當DI解析 Providers 時,都會對提供的每一個 provider 進行規範化處理,即轉換成標準的形式。

function _normalizeProviders(providers: Provider[], res: Provider[]): Provider[] {
  providers.forEach(b => {
    if (b instanceof Type) { // 支持簡潔的語法,轉換爲標準格式
      res.push({provide: b, useClass: b});
    } else if (b && typeof b == 'object' && (b as any).provide !== undefined) {
      res.push(b as NormalizedProvider);
    } else if (b instanceof Array) {
      _normalizeProviders(b, res); // 若是是數組,進行遞歸處理
    } else {
      throw invalidProviderError(b);
    }
  });
  return res;
}

2.建立 Token 時爲了不命名衝突,儘可能避免使用字符串做爲Token。

3.若要建立模塊內通用的依賴對象,須要在 NgModule 中註冊相關的 provider,若在每一個組件中,都有惟一的依賴對象,就須要在 Component 中註冊相關的 provider。

4.multi providers 的具體做用,具體請參考 - Angular2 Multi Providers

5.Provider 是用來描述與 Token 關聯的依賴對象的建立方式。當咱們使用 Token 向 DI 系統獲取與之相關連的依賴對象時,DI 會根據已設置的建立方式,自動的建立依賴對象並返回給使用者。

Provider接口

export interface ClassProvider {
  // 用於設置與依賴對象關聯的Token值,Token值多是Type、InjectionToken、OpaqueToken的實例或字符串
  provide: any; 
  useClass: Type<any>;
  // 用於標識是否multiple providers,如果multiple類型,則返回與Token關聯的依賴對象列表
  multi?: boolean; 
}
  
export interface ValueProvider {
  provide: any;
  useValue: any;
  multi?: boolean;
}
  
export interface ExistingProvider {
  provide: any;
  useExisting: any;
  multi?: boolean;
}
  
export interface FactoryProvider {
  provide: any;
  useFactory: Function;
  deps?: any[]; // 用於設置工廠函數的依賴對象
  multi?: boolean;
}

總結

在文章的最後,想舉一個現實生活中的例子,幫助初學者更好地理解 Angular 2 DI 和 Provider。

Provider 中的 token 能夠理解爲菜名,useClass、useValue能夠理解爲菜的烹飪方式,而依賴對象就是咱們所點的菜,而 DI 系統就是咱們的廚師了。若是沒有廚師,咱們就得關心煮這道菜須要哪些原材料,怎麼煮菜,重要的是還得本身煮,可想而知多麻煩。而有了廚師(DI),咱們只要在菜譜上點菜,必要時備註一下烹飪方式,不過多久香噴噴的菜就上桌鳥~~~。

相關文章
相關標籤/搜索