依賴注入介紹
控制反轉(zhuǎn)(Inversion of Control,縮寫為IoC),是面向?qū)ο?/b>編程中的一種設(shè)計(jì)原則,可以用來(lái)減低計(jì)算機(jī)代碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection,簡(jiǎn)稱DI),還有一種方式叫“依賴查找”(Dependency Lookup)。通過控制反轉(zhuǎn),對(duì)象在被創(chuàng)建的時(shí)候,由一個(gè)調(diào)控系統(tǒng)內(nèi)所有對(duì)象的外界實(shí)體將其所依賴的對(duì)象的引用傳遞給它。也可以說(shuō),依賴被注入到對(duì)象中。
簡(jiǎn)而言之,?沒事你不要來(lái)找我,有事我會(huì)去找你。
Angular依賴注入
首先介紹幾個(gè)簡(jiǎn)單的概念。
1.注入器(Inject):就像制造工廠,提供了一些列的接口用于創(chuàng)建依賴對(duì)象的實(shí)例
2.Provider:用于配置注入器,注入器通過它來(lái)創(chuàng)建被依賴對(duì)象的實(shí)例,Provider把標(biāo)識(shí)映射到工廠方法中,被依賴的對(duì)象就是通過該方法創(chuàng)建的。
3.依賴(Dependence):指定了被依賴對(duì)象的類型,注入器會(huì)根據(jù)此類型創(chuàng)建對(duì)應(yīng)的對(duì)象。
在組件中注入服務(wù)
Angular在底層做了大量的初始化工作,這大大簡(jiǎn)化了創(chuàng)建依賴注入的過程,在組件中使用依賴注入需要完成以下三個(gè)步驟
-通過import導(dǎo)入被依賴對(duì)象的服務(wù)
-在組建中配置注入器。在啟動(dòng)組件時(shí),Angular會(huì)讀取@Component裝飾器里的providers元數(shù)據(jù),它是一個(gè)數(shù)組,配置了該組件需要使用到的所有依賴,Angular的依賴注入框架就會(huì)根據(jù)這個(gè)列表去創(chuàng)建對(duì)應(yīng)對(duì)象的實(shí)例。
-在組件構(gòu)造函數(shù)中聲明所注入的依賴。注入器就會(huì)根據(jù)構(gòu)造函數(shù)上的聲明,在組件初始化時(shí)通過第二步中的providers元數(shù)據(jù)配置依賴,為構(gòu)造函數(shù)提供對(duì)應(yīng)的依賴服務(wù),最終完成注入過程。

在服務(wù)中注入服務(wù)
除了組件服務(wù)依賴,服務(wù)間的相互調(diào)用也很常見。
//logger.service.ts

//contact.service.ts

//在組件的providers元數(shù)據(jù)中注冊(cè)服務(wù)
providers:[LoggerService,ContactService]
在上述中,LoggerService和ContactService這兩個(gè)服務(wù)都用了@Injectable()裝飾器,實(shí)際上它并不是必須的,只有一個(gè)服務(wù)依賴其他服務(wù)時(shí),才需要用@Injectable()顯示裝飾。上述的LoggerService服務(wù)并沒有依賴其他服務(wù),它可以不用@Injectable()裝飾,而ContactService服務(wù)依賴了其他服務(wù),則需要@Injectable()裝飾。
Angular官方推薦無(wú)論是否有依賴其他服務(wù),都應(yīng)該使用@Injectable()來(lái)撞死服務(wù)。一方面,開發(fā)者在給某個(gè)組件注入其他服務(wù)時(shí),無(wú)需再確認(rèn)該服務(wù)是否添加了@Injectable();另一方面,這也是一種良好的團(tuán)隊(duì)協(xié)作方式,整個(gè)團(tuán)隊(duì)遵循相同的開發(fā)原則。
在模塊中注入服務(wù)
在根組件中注入這個(gè)服務(wù),所有子組件都能共享這個(gè)服務(wù)。
在模塊中注入服務(wù)和之前的注入場(chǎng)景稍有不同。Angular在啟動(dòng)程序時(shí)會(huì)啟動(dòng)一個(gè)根模塊,并加載它所依賴的其他模塊,此時(shí)會(huì)生成一個(gè)全局的根注入器,由該注入器創(chuàng)建的依賴注入對(duì)象在整個(gè)應(yīng)用程序級(jí)別可見,并共享一個(gè)實(shí)例。同時(shí)根模塊會(huì)指定一個(gè)根組件并啟動(dòng),由該根組件添加的依賴注入對(duì)象是組件樹級(jí)別可見,在根組件以及子組件中共享一個(gè)實(shí)例。

層級(jí)注入
Angular以組件為基礎(chǔ),項(xiàng)目開發(fā)中自然會(huì)有層級(jí)嵌套的情況,這種組織關(guān)系組成了組件樹。根組件下面的各層級(jí)的子組件,可以出現(xiàn)在任何層級(jí)的任何組件中,每個(gè)組件可以擁有一個(gè)或多個(gè)依賴對(duì)象的注入,每個(gè)依賴對(duì)象對(duì)于注入器而言都是單例。
//生成唯一標(biāo)識(shí)服務(wù)

//子組件A

//子組件B

//父組件

結(jié)果將輸出:
Contact-List
ContactA:0.4500488165839276
ContactB:0.5389674473022938
每個(gè)子組件都創(chuàng)建了自己獨(dú)立的注入器,也就是說(shuō)通過依賴注入的Random服務(wù)都是獨(dú)立的,如果把注入器提升到父組件中,則結(jié)果將會(huì)不一樣。

此時(shí),結(jié)果變?yōu)?/p>
Contact-List
ContactA:0.6257492668005642
ContactB:0.6257492668005642
上述的輸出結(jié)果說(shuō)明了子組件繼承了父組件的注入器,所以子組件使用了相同的Random實(shí)例,輸出了相同的結(jié)果。
那么,該如何選擇在根組件還是在子組件中注入服務(wù)呢?
這取決于想讓注入的依賴服務(wù)具有局部性還是全局性,由于每個(gè)注入器總是將它提供的服務(wù)維持單例,因此,如果不需要針對(duì)每個(gè)組件都提供獨(dú)立的服務(wù)單例,就可以在根組件中注入,整個(gè)組件樹共享根注入器提供的服務(wù)實(shí)例;如果需要針對(duì)每個(gè)組件提供不同的服務(wù)實(shí)例,就應(yīng)該在格子組件中配置providers元數(shù)據(jù)來(lái)注入服務(wù)。
Angular如何查找到合適的服務(wù)實(shí)例呢?
在組件的構(gòu)造函數(shù)視圖注入某個(gè)服務(wù)的時(shí)候,Angular會(huì)先從當(dāng)前組件的注入器中查找,找不到就繼續(xù)往父組件的注入器查找,直到根組件注入器,最后到應(yīng)用根注入器,此時(shí)找不到的話就會(huì)報(bào)錯(cuò)。