首先介紹 Angular 中依賴注入的相關概念:html
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);
}
}
複製代碼
通常不用本身手動注入,Angular 會在啓動過程當中爲你建立全應用級注入器以及所需的其它注入器。數組
是一個對象,告訴 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]
})
複製代碼
provide 屬性提供了provider 的token,也叫令牌,表示在構造函數中指定的類型。 也就是說,當constructor(private productService: ProductService){...} 指定了ProductService,就會去找token是productService的provider。dom
providers: [{provide: ProductService, useClass: ProductService} ]
的簡寫是 providers: [ ProductService ]
useClass屬性指定實例化方式,表示是 new 一個 ProductService,若是userClass" AnotherProductService
真正實例化的就是 AnotherProductService。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 }}
]
複製代碼
provide聲明在App模塊中,則對全部模塊可見ui
provide聲明在某組件中,只對該組件及其子組件可見。其餘組件不能夠注入。 當聲明在組件和模塊中的提供器具備相同的token時,聲明在組件中的提供器會覆蓋模塊中的那個提供器。
表示FooService能夠經過構造函數注入其餘服務 舉個例子,若是註釋掉
// @Injectable({
// providedIn: 'root'
// })
複製代碼
就會報錯
爲何在組件中沒有寫@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()
}
}
複製代碼