Angular 模板解惑

翻譯來(lái)自國(guó)外大神

?上邊是原文鏈接,英語(yǔ)好的可以直接進(jìn),翻譯不周還望指正,共同提高.

Angular

Angular 中的各種模塊是一個(gè)非常復(fù)雜的話題.Angular團(tuán)隊(duì)做了很多來(lái)寫(xiě)了非常長(zhǎng)的關(guān)于NgModule的記錄,可以點(diǎn)擊這里(英文版的), 就是官網(wǎng)的文檔 這里(中文版的),這里邊的文檔提供了比較詳細(xì)的說(shuō)明,你可以在這里找到大多數(shù)你想要的資料,但是這里邊還是漏了一些,所以程序猿們會(huì)因此產(chǎn)生一些誤解.我經(jīng)常看到很多人誤解了這些解釋,用的建議解決方案也不對(duì),因?yàn)樗麄儾粫?huì)到模板在底層是如何工作的.

這篇文章提供了深入的解釋,解析了一些在StackOverflow中經(jīng)常看見(jiàn)的大家可能都有的疑惑,

模塊封裝

Angular 中模塊封裝的概念跟ES 中模塊的概念比較像,可以聲明的類型 比如組件(Component),指令(Directive) ,管道(Pipe), 這些類型可以被當(dāng)前模塊里邊的組件來(lái)用. 舉個(gè)例子:如果你想在App Module 里邊的App Component ,用? A Module 里邊的 a-comp 這個(gè)組件,

code

這里會(huì)報(bào)錯(cuò)的 ,

code-Error

這是因?yàn)樵贏pp module 里邊沒(méi)有聲明 a-comp,如果你想用這個(gè)組件 就需要從定義這個(gè)組件的地方引進(jìn)來(lái),主要思路分兩步: 第一步在A Module中導(dǎo)出ACompont 給別人用., 第二步App Module中引用A Module? ?.代碼大概可以使這樣.

import AModule inAppModule

也就是說(shuō)你需要在App Module 中把A Module 引過(guò)來(lái).這就是模塊封裝的概念,這樣做還不夠,需要在A module導(dǎo)出 a-comp,這樣別的模塊才可以使用.

export AComponent

改完之后 ,可能依舊報(bào)錯(cuò),這時(shí)候需要把服務(wù)重啟下.然后就好了.

當(dāng)然上邊我們操作的是Component ,對(duì)于其他類型, Directive 和Pipe 也是同樣的操作. 先導(dǎo)出再導(dǎo)入.

需要注意一點(diǎn): Angular 沒(méi)有對(duì)entryComponents 中的包含的組件進(jìn)行封裝,如果你用動(dòng)態(tài)Views 和動(dòng)態(tài)組件實(shí)例化, 你可以使用在A Module 中的組件 而不需要將他們加到exports 的數(shù)組中,但是還是需要引用A Module ,相當(dāng)于省去了上邊的第一步 導(dǎo)出的過(guò)程.

?很多剛開(kāi)始用Angular 的朋友可能以為對(duì)于Providers也是像上邊一樣根據(jù)模塊進(jìn)行封裝的, 但是事實(shí)不是這樣滴.在一個(gè)非懶加載的模塊中定義的一個(gè)Provider,在整個(gè)App中都可以訪問(wèn)到.下一章會(huì)解釋為什么是這樣.


層級(jí)模塊

關(guān)于引用模塊最大的困惑是,很多人認(rèn)為這樣就實(shí)現(xiàn)了繼承.比如之前的例子,可能認(rèn)為在App Module 引入A Module ,所以App Module 就成了A Module 的父模塊.然而事實(shí)不是這樣,所有的模塊在編譯階段就被合并了,所以在這里引用模塊(App Module) 和被引用模塊(A Module) 沒(méi)有繼承關(guān)系.

跟Angular 中的Component 一樣,Angular 會(huì)在根模塊生成一個(gè)工廠 (Factory),根模塊就是你在main.ts的?platformBrowserDynamic().bootstrapModule 指出的.

root Module

Angular 編譯器生成的這個(gè)工廠(Factory)用?createNgModuleFactory?方法,需要傳幾個(gè)參數(shù),

1,對(duì)模塊類的引用,?module class reference

2,根組件,?bootstrap components

3,帶有entry Componnet的 Component Factory Resolver,component factory resolver with entry components(后來(lái)木有了)

4,把所有模塊中的Providers 合并之后的工廠,definition factory with merged module providers

createNgModuleFactory

我看了下參數(shù),主要是三個(gè) : 模塊類型,跟組件,工廠的定義,

最后兩個(gè)3,4 加粗的地方,解釋了為什么這里沒(méi)有針對(duì)Provider 和entryComponents 的模塊封裝,是因?yàn)锳ngular 編譯之后,之前是多個(gè)Module ,編譯之后就成了一個(gè)Final Module. 這個(gè)Final Module 中包含 合并之后的所有Module. 在編譯的時(shí)候, Angular 編譯器不知道你在哪或者你想怎么來(lái)使用Provider 和動(dòng)態(tài)組件,所以他不能控制Provider 和動(dòng)態(tài)組件的封裝,但是當(dāng)解析組件的模板時(shí),Angular知道他們是如何使用的 ,比如私有可聲明組件,指令和管道.

下面看一個(gè)Module 生成工廠的例子,假設(shè)你有A和B 兩個(gè)模塊,每個(gè)模塊中都定義了一個(gè)Provider 和EntryComponent,

在A Module中定義一個(gè)Provider 和entry Components

AModule

在B Module中定義一個(gè)Provider 和entry Components,這個(gè)跟A 比較像

BModule????

在App Module中引入A B Module,并且定義自己的 Provider "root"

AppModule

當(dāng)編譯器為App root 模塊來(lái)生成剛才說(shuō)的模塊工廠的時(shí)候, 這個(gè)工廠會(huì)對(duì)所有模塊中的Provider進(jìn)行合并,然后生成一個(gè)factory只對(duì)最后的這個(gè)final module,也就是說(shuō)這個(gè)factory 里邊包含了所有的Provider ,

你可以在Chrome 中打開(kāi)F12 開(kāi)發(fā)者工具中,選擇Souces 標(biāo)簽,然后左邊選擇ng:// 打開(kāi)AppModule里邊的module.ngfactory.js.

ngfactory.js

你可以看見(jiàn)所有的Provider 和entry Component 都被合并了 ,并且傳入了moduleDef 這個(gè)方法,所有不管你創(chuàng)建了多少個(gè)Module ,最后都是生成了一個(gè)Factory ,在這個(gè)Factory 里邊包含了所有合并的Provider. 這個(gè)factory 用來(lái)創(chuàng)建帶有自己injector的module實(shí)例. 因?yàn)槲覀冏詈蟮玫搅艘粋€(gè)合并后的Module,Angular會(huì)創(chuàng)建一個(gè)根Injector來(lái)使用這些Provider.

現(xiàn)在你可能疑惑 如果有兩個(gè)Module 里邊 有相同的provider token ,那會(huì)發(fā)生什么?

總結(jié)就是兩條規(guī)則. 第一個(gè)條 就是就近原則.?

因?yàn)樵贏 Module中定義過(guò)??providers:[{provide:'a',useValue:'a'}],?

這時(shí)候如果 App Module 中也定義一個(gè)??providers: [{provide:'a', useValue:'root'}],?

最后生成的是這樣 :? 也就是App 本身的a 覆蓋了 A Module 中的a ,自己有了就不需要?jiǎng)e人幫忙, 就近原則.?

ngfactory.js

第二條就是覆蓋原則. 占山為王原則,原本有個(gè)土匪頭子 ,后來(lái)又來(lái)了個(gè)土匪頭子干掉了第一個(gè), 那這個(gè)山頭就是第二個(gè)土匪頭子的.

比如在Bmodule 中是這樣寫(xiě)的 , 為了覆蓋A Module 中的a provider

providers:[{provide:'a',useValue:'b'}],

? entryComponents:[BComponent]

在App 中引用的順序是這樣,

@NgModule({

imports: [AModule, BModule],

...

})

export class AppModule {}?

也就是B Module 在Amodule 之后 , 那B 這個(gè)土匪頭子就把之前的干掉 ,

生成之后的ModuleDef 就是這樣的

你要是把順序反過(guò)來(lái) , A 干掉了B? 就是這樣.

懶加載模塊

Angular 本身通過(guò)路由提供模塊懶加載的功能,不熟悉的可以參照Angular懶加載

Angular creates a lazy-loaded module with its own injector, a?child?of the root injector… So a lazy-loaded module that imports that shared module makes its own copy of the service.

字面意思是:Angular對(duì)于懶加載的模塊會(huì)給他們生成自己的Injector ,而懶加載中的這些provider 是沒(méi)有被打平合并到之前factory 生成的final modules 里邊. Angular 本身會(huì)給這些懶加載的模塊創(chuàng)建一個(gè) 獨(dú)立的Factory.? 如果懶加載模塊中的provider token (就是那個(gè)Service 名字) 跟 final modules 里邊的一樣, 那么Angular 會(huì)重新創(chuàng)建一個(gè) provider 實(shí)例.

換句話說(shuō)就是在懶加載模塊中 ,懶加載模塊是被之前說(shuō)的 那個(gè) Angular 的編譯器給一起打成那個(gè)final modules 一起,但是懶加載模塊中的injector 用的是? 從parentInjector 獲得之后重新create 的.

文中標(biāo)紅代碼

forRoot( ) 和 forChild() 方法

首先說(shuō)我們常用的 在RouterModule中的這兩個(gè)方法:

forRoot() :?static forRoot(routes: Routes, config?: ExtraOptions): ModuleWithProviders<RouterModule>

forChild():?static forChild(routes: Routes): ModuleWithProviders<RouterModule>

表面看上去 這兩個(gè) 區(qū)別就是在于forRoot() 方法有一個(gè)可選的參數(shù),對(duì)其進(jìn)行一些配置.兩個(gè)方法都返回的是ModuleWithProviders,

forRoot是用在根模塊加載路由配置,而forChild是用在子模塊加載路由配置.那么除了這些還有什么區(qū)別?

官方解釋是這樣: 因?yàn)閒orRoot() creates a module that contains all the directives, the given routes, and the router service itself.

forChild() creates a module that contains all the directives and the given routes, but does not include the router service.

也就是forRoot() 里包含了router Service 在forChild()里邊沒(méi)有router service . why ? 我們知道懶加載的模塊會(huì)重新生成一個(gè)injector ,如果我們?cè)谝粋€(gè)app 中有多個(gè)router Service ,他們就會(huì)共用 一個(gè)資源 Location. 我們因此不能創(chuàng)建多個(gè)router Service.

對(duì)于其他的Module 除了剛才的例子里邊的RouterModule.

forRoot 來(lái)給App module 用. forchild()來(lái)給 懶加載模塊來(lái)用.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。