使用Angular的依賴注入

首先介紹 Angular 中依賴注入的相關概念:html

Service 服務

Service 的表現形式是一個class,能夠用來在組件中複用 好比 Http 請求獲取數據,日誌處理,驗證用戶輸入等都寫成Service,供組件使用。bootstrap

import { Injectable } from '@angular/core';
// 在 Angular 中,要把一個類定義爲服務,就要用 `@Injectable` 裝飾器來提供元數據

@Injectable({
  // we declare that this service should be created
  // by the root application injector.
  providedIn: 'root',
})
export class LoggerService {
  warn(msg) { 
    return console.warn(msg); 
  }
}

複製代碼

Injector 注入器

通常不用本身手動注入,Angular 會在啓動過程當中爲你建立全應用級注入器以及所需的其它注入器。數組

Provider 提供商

是一個對象,告訴 Injector 應該如何獲取或建立依賴。bash

打開Angular看下面的代碼片斷 app.module.tsapp

@NgModule({
  declarations: [
    ....
  ],
  imports: [
    ....
  ],
  // providers 告訴 Angular 應用哪些對象須要依賴注入
  // providers 是個數組,每一項都是provider
  providers: [
    //  簡寫,等價 {provider: LoggerService, useClass: LoggerService}
    LoggerService,
    { 
       provide: RequestCache, 
       useClass: RequestCacheWithLocalStorage 
    },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: UrlInterceptor,
      multi: true
    },
  ],
  bootstrap: [AppComponent]
})
複製代碼

DI token(令牌)

provide 屬性提供了provider 的token,也叫令牌,表示在構造函數中指定的類型。 也就是說,當constructor(private productService: ProductService){...} 指定了ProductService,就會去找token是productService的provider。dom

image.png

Provider 的幾種寫法
  1. useClass providers: [{provide: ProductService, useClass: ProductService} ] 的簡寫是 providers: [ ProductService ] useClass屬性指定實例化方式,表示是 new 一個 ProductService,若是userClass" AnotherProductService 真正實例化的就是 AnotherProductService。
  2. userFactory 除了useClass寫法,還可使用 userFactory 工廠方法,這個方法返回的實例做爲構造函數中productService參數的內容。 providers: [{provide: ProductService, userFactory: () => {}} ] 這樣能夠根據條件具體實例化某對象,更加靈活
providers: [{
  provide: ProductService, 
  userFactory: () => {
    let logger = new LoggerService();
    let dev = Math.random() > 0.5;
    if (dev) {
      return new ProductService(logger);
    } else {
      return new AnotherProductService(logger);
   }
  }
}, LoggerService ]
複製代碼

上面的寫法有個弊端LoggerService和ProductService耦合太強 進一步優化,利用deps參數,指工廠聲明所依賴的參數。ide

providers: [{
    provide: ProductService, 
    userFactory: (logger: LoggerService) => {
      let dev = Math.random() > 0.5;
      if (dev) {
        return new ProductService(logger);
      } else {
        return new AnotherProductService(logger);
     }
    },
    deps: [ LoggerService ]
  }, 
  LoggerService
]
複製代碼

再次優化,定義第三個提供器,token是IS_DEV_ENV,值是具體的false函數

providers: [{
    provide: ProductService, 
    // 注入的 順序和deps對應
    userFactory: (logger: LoggerService, isDev) => {
      if (isDev) {
        return new ProductService(logger);
      } else {
        return new AnotherProductService(logger);
     }
    },
    deps: [ LoggerService, 'IS_DEV_ENV' ]
  }, 
  LoggerService,
  {provide: 'IS_DEV_ENV', useValue: false}
]
複製代碼

通常來講能夠建立一個類型爲對象的提供器供注入優化

providers: [{
    provide: ProductService, 
    // 注入的 順序和deps對應
    userFactory: (logger: LoggerService, appConfig) => {
      if (appConfig.isDev) {
        return new ProductService(logger);
      } else {
        return new AnotherProductService(logger);
     }
    },
    deps: [ LoggerService, 'APP_CONFIG' ]
  }, 
  LoggerService,
  { provide: 'APP_CONFIG', useValue: {isDev: false }}
]
複製代碼

提供器的做用域

image.png

provide聲明在App模塊中,則對全部模塊可見ui

provide聲明在某組件中,只對該組件及其子組件可見。其餘組件不能夠注入。 當聲明在組件和模塊中的提供器具備相同的token時,聲明在組件中的提供器會覆蓋模塊中的那個提供器。

@Injectable 裝飾器

表示FooService能夠經過構造函數注入其餘服務 舉個例子,若是註釋掉

// @Injectable({
//   providedIn: 'root'
// })
複製代碼

就會報錯

image.png

image.png

爲何在組件中沒有寫@Injectable也能直接注入service? 咱們知道定義組件要寫@Component裝飾器,定義管道要寫@Pipe裝飾器,他們都是Injectable的子類。 同時Component又是Directive的子類,因此全部的組件都是指令。

手動注入

import { Component, OnInit, Injector } from '@angular/core';
import { LoggerService } from '../_service/logger.service';

@Component({
  selector: 'app-di',
  templateUrl: './di.component.html',
  styleUrls: ['./di.component.styl']
})
export class DIComponent implements OnInit {
  logger: LoggerService;
  // 手動注入
  constructor(
    private injector: Injector
  ) {
    this.logger = injector.get(LoggerService);
  }

  ngOnInit() {
    this.logger.log()
  }
}

複製代碼
相關文章
相關標籤/搜索