曾自己借助阿里云和hexo搭了個站點(diǎn),現(xiàn)已廢棄,過往寫的博客暫挪到此處漓雅。
title: Angular2依賴注入
subtitle: 小白學(xué)Angular2
date: 2016-11-24 22:53:51
tags:
- 技術(shù)
- Angular2
- 學(xué)習(xí)筆記
依賴注入用我這個小白的話說衣迷,就是定義一個類的時候,將某些依賴作為類的參數(shù)邮破,即:
var myClass = function( arg1,arg2){
this.arg1 = arg1;
this.arg2 = arg2;
}
初始化該類的時候诈豌,就可以將這些依賴當(dāng)做參數(shù)傳遞進(jìn)去仆救。
這樣,當(dāng)arg1和arg2改變的時候矫渔,我們可以不用動這個myClass內(nèi)部的代碼彤蔽。
這個用法在純js時代就常用,只是現(xiàn)在叫做依賴注入庙洼。目的是為了顿痪,解耦。
angular 依賴注入與 我的myClass的不同在于
如果arg1和arg2都是類的實(shí)例送膳,
myClass需要先實(shí)例化這些類员魏,然后再調(diào)用new myClass(instance1,instance2)。
而Angular叠聋,不需要先實(shí)例化每一個依賴注入的類撕阎。Angular自己會幫你實(shí)例化。但是需要以下幾步
構(gòu)造函數(shù)參數(shù)中有這些類: constructor(heroService:HeroService)
這個constructor的類有一個@Component裝飾器(這個一般都不必說碌补。虏束。)
@Component中有這些類的providers信息,即providers:[HeroService]
對于被依賴的那些類厦章,需要在export class之前加上@Injectable()镇匀,否則注入器會報(bào)錯
注:服務(wù)是可以繼承的
Angular中的顯性注入器
首先這應(yīng)當(dāng)是沒有必要的,只要正確使用依賴注入袜啃,angular自己會管理好注入器的創(chuàng)建和調(diào)用汗侵。
當(dāng)方式如下:
injector = ReflectiveInjector.resolveAndCreate([Car, Engine, Tires]);
let car = injector.get(Car);
直接使用注入器Injector的方式
可以直接使用注入器工作,但不方便閱讀群发,難以理解晰韵,所以盡量避免:
在@Component中添加這些類的providers信息,即providers:[HeroService]
在constructor中注入Injector(Injector本身就是一個可注入的服務(wù))
然后在Component中通過injector.get方式獲得providers提供的服務(wù)(即類HeroService)
如果get失敗會拋出異常熟妓,get方法可以帶第二個參數(shù)雪猪,表示如果服務(wù)沒找到就當(dāng)做默認(rèn)值返回。
注入器的提供商們:
令牌token:它作為鍵值key使用起愈,用于定位依賴值只恨,以及注冊這個提供商
providers: [Logger]
provider definition object:知道如何創(chuàng)建依賴值得配方,有很多方式創(chuàng)建依賴值
[{ provide: Logger, useClass: Logger }]
1. 備選的類提供商
[{ provide: Logger, useClass: BetterLogger }]
當(dāng)請求Logger時抬虽,提供BetterLogger
(不同于別名官觅,見3)
2. 帶依賴的類提供商
[ UserService,
{ provide: Logger, useClass: EvenBetterLogger }]
因?yàn)镋venBetterLogger注入了UserService(?阐污?可是為什么不會在EvenBetterLogger中自動調(diào)用休涤??還要在調(diào)用EvenBetterLogger的地方在provide聲明一下這個服務(wù)疤剑。)
3. 別名類提供商
[ NewLogger,
// Alias OldLogger w/ reference to NewLogger
{ provide: OldLogger, useExisting: NewLogger}]
對比1滑绒,1會創(chuàng)建兩個實(shí)例闷堡,(?疑故?1在什么時候回用到杠览??需要創(chuàng)建兩個實(shí)例)
[ NewLogger,
// Not aliased! Creates two instances of `NewLogger`
{ provide: OldLogger, useClass: NewLogger}]
4. 值提供商
[{ provide: Logger, useValue: silentLogger }]
silentLogger是一個對象
5. 工廠提供商
在需要動態(tài)創(chuàng)建依賴值得情況下纵势,使用此方式
//deps為該服務(wù)需要的依賴踱阿,通過注入器傳入工廠方法
export let heroServiceProvider =
{ provide: HeroService,
useFactory: heroServiceFactory,
deps: [Logger, UserService]
};
let heroServiceFactory = (logger: Logger, userService: UserService) => {
return new HeroService(logger, userService.user.isAuthorized);
};
//滿足的場景
constructor(
private logger: Logger,
private isAuthorized: boolean) { }
getHeroes() {
let auth = this.isAuthorized ? 'authorized ' : 'unauthorized';
this.logger.log(`Getting heroes for ${auth} user.`);
return HEROES.filter(hero => this.isAuthorized || !hero.isSecret);
}
注:我們?yōu)榱酥貜?fù)利用導(dǎo)出一個變量捕獲了這個工廠提供商:heroServiceProvider。但也可以不用導(dǎo)出再使用钦铁,直接在需要該服務(wù)的地方的provider里加上即可软舌,同1、2牛曹、3佛点、4
依賴注入令牌
注入器維護(hù)一個內(nèi)部的令牌-提供商映射表。
對于類而言黎比,類名就是這個令牌超营。
非類依賴
export interface AppConfig {
apiEndpoint: string;
title: string;
}
export const HERO_DI_CONFIG: AppConfig = {
apiEndpoint: 'api.heroes.com',
title: 'Dependency Injection'
};
TypeScript接口不是一個有效的令牌。
Angular不支持阅虫。
解釋為:在強(qiáng)類型語言中接口是首選的用于查找依賴的主鍵演闭,再使用依賴注入很奇怪。
TS在生成JS之后颓帝,不會再有接口米碰。
Opaque Token
解決方案是使用一個Opaque Token(不透明的令牌)
import { OpaqueToken } from '@angular/core';
export let APP_CONFIG = new OpaqueToken('app.config');
使用OpaqueToken對象注冊依賴的提供商
providers: [{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }]
在@Inject的幫助,將這個配置對象注入到需要的構(gòu)造函數(shù)中
constructor(@Inject(APP_CONFIG) config: AppConfig) {
this.title = config.title;
}
可選依賴
import { Optional } from '@angular/core’;
constructor(@Optional() private logger: Logger) {
if (this.logger) {
this.logger.log(some_message);
}
}
注:當(dāng)使用@Optional()的時候购城,需要為空值做準(zhǔn)備
進(jìn)階:
多級依賴注入器
angular的多級依賴注入系統(tǒng)支持與組件樹并行的嵌套式注入器
大白話吕座,子組件通過冒泡的方式向上查找需要的服務(wù);
其次工猜,每個注入器都會把它提供的服務(wù)處理成單例米诉,但當(dāng)我們并不想共享菱蔬,想為單個組件生成一個實(shí)例篷帅,則不要把服務(wù)注入到父級的以及父級的父級的組件中。
其他規(guī)范:
建議為每個服務(wù)類都添加@Injectable()拴泌,無論是否被依賴魏身,出于為了以后可能被依賴著想以及一致性
@Component和@Directive @Pipe都是InjectableMetadata的子類型,所以沒有必要為component添加@Injectable方法
注入器可以從編譯后的Javascript代碼中讀取類的元數(shù)據(jù)蚪腐,并使用構(gòu)造函數(shù)的參數(shù)類型信息來決定注入什么箭昵。
(不是每個JavaScript類都有元數(shù)據(jù)。TS編輯器默認(rèn)忽視元數(shù)據(jù)回季。如果tsconfig.json中的emitDecoratorMetadata編譯器選項(xiàng)為true家制,編譯器就會在生成的Javascript中為每個至少擁有一個裝飾器的類添加元數(shù)據(jù)正林。
注入器使用一個類的構(gòu)造元數(shù)據(jù)來決定依賴類型(?颤殴?依賴類型有些什么觅廓??)涵但,該構(gòu)造元數(shù)據(jù)就是構(gòu)造函數(shù)的參數(shù)類型所標(biāo)識的杈绸。TS為任何帶有一個裝飾器的類生成這樣的元數(shù)據(jù),任何裝飾器都生成矮瘟。當(dāng)然瞳脓,使用一個合適的Injectable裝飾器來標(biāo)識更有意義。