99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

Angular9 NgModule 常見(jiàn)問(wèn)題

2020-07-03 14:40 更新

NgModules 可以幫你把應(yīng)用組織成一些緊密相關(guān)的代碼塊。

這里回答的是開(kāi)發(fā)者常問(wèn)起的關(guān)于 NgModule設(shè)計(jì)與實(shí)現(xiàn)問(wèn)題。

我應(yīng)該把哪些類加到 declarations 中?

把可聲明的類(組件、指令和管道)添加到 declarations 列表中。

這些類只能在應(yīng)用程序的一個(gè)并且只有一個(gè)模塊中聲明。 只有當(dāng)它們從屬于某個(gè)模塊時(shí),才能把在此模塊中聲明它們。

什么是可聲明的?

聲明的就是組件、指令和管道這些可以被加到模塊的 declarations 列表中的類。它們也是所有能被加到 declarations 中的類。

哪些類不應(yīng)該加到 declarations 中?

只有可聲明的類才能加到模塊的 declarations 列表中。

不要聲明:

  • 已經(jīng)在其它模塊中聲明過(guò)的類。無(wú)論它來(lái)自應(yīng)用自己的模塊(@NgModule)還是第三方模塊。

  • 從其它模塊中導(dǎo)入的指令。例如,不要聲明來(lái)自 @angular/formsFORMS_DIRECTIVES,因?yàn)?FormsModule 已經(jīng)聲明過(guò)它們了。

  • 模塊類。

  • 服務(wù)類

  • 非 Angular 的類和對(duì)象,比如:字符串、數(shù)字、函數(shù)、實(shí)體模型、配置、業(yè)務(wù)邏輯和輔助類。

為什么要把同一個(gè)組件聲明在不同的 NgModule 屬性中?

AppComponent 經(jīng)常被同時(shí)列在 declarationsbootstrap 中。 另外你還可能看到 HeroComponent 被同時(shí)列在 declarations、exportsentryComponent 中。

這看起來(lái)是多余的,不過(guò)這些函數(shù)具有不同的功能,從它出現(xiàn)在一個(gè)列表中無(wú)法推斷出它也應(yīng)該在另一個(gè)列表中。

  • AppComponent 可能被聲明在此模塊中,但可能不是引導(dǎo)組件。

  • AppComponent 可能在此模塊中引導(dǎo),但可能是由另一個(gè)特性模塊聲明的。

  • 某個(gè)組件可能是從另一個(gè)應(yīng)用模塊中導(dǎo)入的(所以你沒(méi)法聲明它)并且被當(dāng)前模塊重新導(dǎo)出。

  • 某個(gè)組件可能被導(dǎo)出,以便用在外部組件的模板中,也可能同時(shí)被一個(gè)彈出式對(duì)話框加載。

"Can't bind to 'x' since it isn't a known property of 'y'"是什么意思?

這個(gè)錯(cuò)誤通常意味著你或者忘了聲明指令“x”,或者你沒(méi)有導(dǎo)入“x”所屬的模塊。

如果“x”其實(shí)不是屬性,或者是組件的私有屬性(比如它不帶 @Input@Output 裝飾器),那么你也同樣會(huì)遇到這個(gè)錯(cuò)誤。

我應(yīng)該導(dǎo)入什么?

導(dǎo)入你需要在當(dāng)前模塊的組件模板中使用的那些公開(kāi)的(被導(dǎo)出的)可聲明類。

這意味著要從 @angular/common 中導(dǎo)入 CommonModule 才能訪問(wèn) Angular 的內(nèi)置指令,比如 NgIfNgFor。 你可以直接導(dǎo)入它或者從重新導(dǎo)出過(guò)該模塊的其它模塊中導(dǎo)入它。

如果你的組件有 [(ngModel)] 雙向綁定表達(dá)式,就要從 @angular/forms 中導(dǎo)入 FormsModule。

如果當(dāng)前模塊中的組件包含了共享模塊和特性模塊中的組件、指令和管道,就導(dǎo)入這些模塊。

只能在根模塊 AppModule 中導(dǎo)入 BrowserModule。

我應(yīng)該導(dǎo)入 BrowserModule 還是 CommonModule?

幾乎所有要在瀏覽器中使用的應(yīng)用的根模塊(AppModule)都應(yīng)該從 @angular/platform-browser 中導(dǎo)入 BrowserModule

BrowserModule 提供了啟動(dòng)和運(yùn)行瀏覽器應(yīng)用的那些基本的服務(wù)提供者。

BrowserModule 還從 @angular/common 中重新導(dǎo)出了 CommonModule,這意味著 AppModule 中的組件也同樣可以訪問(wèn)那些每個(gè)應(yīng)用都需要的 Angular 指令,如 NgIfNgFor

在其它任何模塊中都不要導(dǎo)入BrowserModule。 特性模塊和惰性加載模塊應(yīng)該改成導(dǎo)入 CommonModule。 它們需要通用的指令。它們不需要重新初始化全應(yīng)用級(jí)的提供者。

如果我兩次導(dǎo)入同一個(gè)模塊會(huì)怎么樣?

沒(méi)有任何問(wèn)題。當(dāng)三個(gè)模塊全都導(dǎo)入模塊'A'時(shí),Angular 只會(huì)首次遇到時(shí)加載一次模塊'A',之后就不會(huì)這么做了。

無(wú)論 A 出現(xiàn)在所導(dǎo)入模塊的哪個(gè)層級(jí),都會(huì)如此。 如果模塊'B'導(dǎo)入模塊'A'、模塊'C'導(dǎo)入模塊'B',模塊'D'導(dǎo)入 [C, B, A],那么'D'會(huì)觸發(fā)模塊'C'的加載,'C'會(huì)觸發(fā)'B'的加載,而'B'會(huì)加載'A'。 當(dāng) Angular 在'D'中想要獲取'B''A'時(shí),這兩個(gè)模塊已經(jīng)被緩存過(guò)了,可以立即使用。

Angular 不允許模塊之間出現(xiàn)循環(huán)依賴,所以不要讓模塊'A'導(dǎo)入模塊'B',而模塊'B'又導(dǎo)入模塊'A'。

特性模塊中導(dǎo)入 CommonModule 可以讓它能用在任何目標(biāo)平臺(tái)上,不僅是瀏覽器。那些跨平臺(tái)庫(kù)的作者應(yīng)該喜歡這種方式的。

我應(yīng)該導(dǎo)出什么?

導(dǎo)出那些其它模塊希望在自己的模板中引用的可聲明類。這些也是你的公共類。 如果你不導(dǎo)出某個(gè)類,它就是私有的,只對(duì)當(dāng)前模塊中聲明的其它組件可見(jiàn)。

你可以導(dǎo)出任何可聲明類(組件、指令和管道),而不用管它是聲明在當(dāng)前模塊中還是某個(gè)導(dǎo)入的模塊中。

你可以重新導(dǎo)出整個(gè)導(dǎo)入過(guò)的模塊,這將導(dǎo)致重新導(dǎo)出它們導(dǎo)出的所有類。重新導(dǎo)出的模塊甚至不用先導(dǎo)入。

我不應(yīng)該導(dǎo)出什么?

不要導(dǎo)出:

  • 那些你只想在當(dāng)前模塊中聲明的那些組件中使用的私有組件、指令和管道。如果你不希望任何模塊看到它,就不要導(dǎo)出。

  • 不可聲明的對(duì)象,比如服務(wù)、函數(shù)、配置、實(shí)體模型等。

  • 那些只被路由器或引導(dǎo)函數(shù)動(dòng)態(tài)加載的組件。 比如入口組件可能從來(lái)不會(huì)在其它組件的模板中出現(xiàn)。 導(dǎo)出它們沒(méi)有壞處,但也沒(méi)有好處。

  • 純服務(wù)模塊沒(méi)有公開(kāi)(導(dǎo)出)的聲明。 例如,沒(méi)必要重新導(dǎo)出 HttpClientModule,因?yàn)樗粚?dǎo)出任何東西。 它唯一的用途是一起把 http 的那些服務(wù)提供者添加到應(yīng)用中。

我可以重新導(dǎo)出類和模塊嗎?

毫無(wú)疑問(wèn)!

模塊是從其它模塊中選取類并把它們重新導(dǎo)出成統(tǒng)一、便利的新模塊的最佳方式。

模塊可以重新導(dǎo)出其它模塊,這會(huì)導(dǎo)致重新導(dǎo)出它們導(dǎo)出的所有類。 Angular 自己的 BrowserModule 就重新導(dǎo)出了一組模塊,例如:

exports: [CommonModule, ApplicationModule]

模塊還能導(dǎo)出一個(gè)組合,它可以包含自己的聲明、某些導(dǎo)入的類以及導(dǎo)入的模塊。

不要費(fèi)心去導(dǎo)出純服務(wù)類。 純服務(wù)類的模塊不會(huì)導(dǎo)出任何可供其它模塊使用的可聲明類。 例如,不用重新導(dǎo)出 HttpClientModule,因?yàn)樗鼪](méi)有導(dǎo)出任何東西。 它唯一的用途是把那些 http 服務(wù)提供者一起添加到應(yīng)用中。

forRoot()方法是什么?

靜態(tài)方法 forRoot() 是一個(gè)約定,它可以讓開(kāi)發(fā)人員更輕松的配置模塊的想要單例使用的服務(wù)及其提供者。RouterModule.forRoot() 就是一個(gè)很好的例子。

應(yīng)用把一個(gè) Routes 對(duì)象傳給 RouterModule.forRoot(),為的就是使用路由配置全應(yīng)用級(jí)的 Router 服務(wù)。 RouterModule.forRoot() 返回一個(gè)ModuleWithProviders對(duì)象。 你把這個(gè)結(jié)果添加到根模塊 AppModuleimports 列表中。

只能在應(yīng)用的根模塊 AppModule 中調(diào)用并導(dǎo)入 forRoot() 的結(jié)果。 在其它模塊,特別是惰性加載模塊中,不要導(dǎo)入它。 要了解關(guān)于 forRoot() 的更多信息,參見(jiàn)單例服務(wù)一章的 the forRoot() 模式部分。

對(duì)于服務(wù)來(lái)說(shuō),除了可以使用 forRoot() 外,更好的方式是在該服務(wù)的 @Injectable() 裝飾器中指定 providedIn: 'root',它讓該服務(wù)自動(dòng)在全應(yīng)用級(jí)可用,這樣它也就默認(rèn)是單例的。

RouterModule 也提供了靜態(tài)方法 forChild(),用于配置惰性加載模塊的路由。

forRoot()forChild() 都是約定俗成的方法名,它們分別用于在根模塊和特性模塊中配置服務(wù)。

當(dāng)你寫(xiě)類似的需要可配置的服務(wù)提供者時(shí),請(qǐng)遵循這個(gè)約定。

為什么服務(wù)提供者在特性模塊中的任何地方都是可見(jiàn)的?

列在引導(dǎo)模塊的 @NgModule.providers 中的服務(wù)提供者具有全應(yīng)用級(jí)作用域。 往 NgModule.providers 中添加服務(wù)提供者將導(dǎo)致該服務(wù)被發(fā)布到整個(gè)應(yīng)用中。

當(dāng)你導(dǎo)入一個(gè)模塊時(shí),Angular 就會(huì)把該模塊的服務(wù)提供者(也就是它的 providers 列表中的內(nèi)容)加入該應(yīng)用的根注入器中。

這會(huì)讓該提供者對(duì)應(yīng)用中所有知道該提供者令牌(token)的類都可見(jiàn)。

通過(guò) NgModule 導(dǎo)入來(lái)實(shí)現(xiàn)可擴(kuò)展性是 NgModule 體系的主要設(shè)計(jì)目標(biāo)。 把 NgModule 的提供者并入應(yīng)用程序的注入器可以讓庫(kù)模塊使用新的服務(wù)來(lái)強(qiáng)化應(yīng)用程序變得更容易。 只要添加一次 HttpClientModule,那么應(yīng)用中的每個(gè)組件就都可以發(fā)起 Http 請(qǐng)求了。

不過(guò),如果你期望模塊的服務(wù)只對(duì)那個(gè)特性模塊內(nèi)部聲明的組件可見(jiàn),那么這可能會(huì)帶來(lái)一些不受歡迎的意外。 如果 HeroModule 提供了一個(gè) HeroService,并且根模塊 AppModule 導(dǎo)入了 HeroModule,那么任何知道 HeroService類型的類都可能注入該服務(wù),而不僅是在 HeroModule 中聲明的那些類。

要限制對(duì)某個(gè)服務(wù)的訪問(wèn),可以考慮惰性加載提供該服務(wù)的 NgModule。

為什么在惰性加載模塊中聲明的服務(wù)提供者只對(duì)該模塊自身可見(jiàn)?

和啟動(dòng)時(shí)就加載的模塊中的提供者不同,惰性加載模塊中的提供者是局限于模塊的。

當(dāng) Angular 路由器惰性加載一個(gè)模塊時(shí),它創(chuàng)建了一個(gè)新的運(yùn)行環(huán)境。 那個(gè)環(huán)境擁有自己的注入器,它是應(yīng)用注入器的直屬子級(jí)。

路由器把該惰性加載模塊的提供者和它導(dǎo)入的模塊的提供者添加到這個(gè)子注入器中。

這些提供者不會(huì)被擁有相同令牌的應(yīng)用級(jí)別提供者的變化所影響。 當(dāng)路由器在惰性加載環(huán)境中創(chuàng)建組件時(shí),Angular 優(yōu)先使用惰性加載模塊中的服務(wù)實(shí)例,而不是來(lái)自應(yīng)用的根注入器的。

如果兩個(gè)模塊提供了同一個(gè)服務(wù)會(huì)怎么樣?

當(dāng)同時(shí)加載了兩個(gè)導(dǎo)入的模塊,它們都列出了使用同一個(gè)令牌的提供者時(shí),后導(dǎo)入的模塊會(huì)“獲勝”,這是因?yàn)檫@兩個(gè)提供者都被添加到了同一個(gè)注入器中。

當(dāng) Angular 嘗試根據(jù)令牌注入服務(wù)時(shí),它使用第二個(gè)提供者來(lái)創(chuàng)建并交付服務(wù)實(shí)例。

每個(gè)注入了該服務(wù)的類獲得的都是由第二個(gè)提供者創(chuàng)建的實(shí)例。 即使是聲明在第一個(gè)模塊中的類,它取得的實(shí)例也是來(lái)自第二個(gè)提供者的。

如果模塊 A 提供了一個(gè)使用令牌'X'的服務(wù),并且導(dǎo)入的模塊 B 也用令牌'X'提供了一個(gè)服務(wù),那么模塊 A 中定義的服務(wù)“獲勝”了。

由根 AppModule 提供的服務(wù)相對(duì)于所導(dǎo)入模塊中提供的服務(wù)有優(yōu)先權(quán)。換句話說(shuō):AppModule 總會(huì)獲勝。

我應(yīng)該如何把服務(wù)的范圍限制到模塊中?

如果一個(gè)模塊在應(yīng)用程序啟動(dòng)時(shí)就加載,它的 @NgModule.providers 具有全應(yīng)用級(jí)作用域。 它們也可用于整個(gè)應(yīng)用的注入中。

導(dǎo)入的提供者很容易被由其它導(dǎo)入模塊中的提供者替換掉。 這雖然是故意這樣設(shè)計(jì)的,但是也可能引起意料之外的結(jié)果。

作為一個(gè)通用的規(guī)則,應(yīng)該只導(dǎo)入一次帶提供者的模塊,最好在應(yīng)用的根模塊中。 那里也是配置、包裝和改寫(xiě)這些服務(wù)的最佳位置。

假設(shè)模塊需要一個(gè)定制過(guò)的 HttpBackend,它為所有的 Http 請(qǐng)求添加一個(gè)特別的請(qǐng)求頭。 如果應(yīng)用中其它地方的另一個(gè)模塊也定制了 HttpBackend 或僅僅導(dǎo)入了 HttpClientModule,它就會(huì)改寫(xiě)當(dāng)前模塊的 HttpBackend 提供者,丟掉了這個(gè)特別的請(qǐng)求頭。 這樣服務(wù)器就會(huì)拒絕來(lái)自該模塊的請(qǐng)求。

要消除這個(gè)問(wèn)題,就只能在應(yīng)用的根模塊 AppModule 中導(dǎo)入 HttpClientModule

如果你必須防范這種“提供者腐化”現(xiàn)象,那就不要依賴于“啟動(dòng)時(shí)加載”模塊的 providers。

只要可能,就讓模塊惰性加載。 Angular 給了惰性加載模塊自己的子注入器。 該模塊中的提供者只對(duì)由該注入器創(chuàng)建的組件樹(shù)可見(jiàn)。

如果你必須在應(yīng)用程序啟動(dòng)時(shí)主動(dòng)加載該模塊,就改成在組件中提供該服務(wù)。

繼續(xù)看這個(gè)例子,假設(shè)某個(gè)模塊的組件真的需要一個(gè)私有的、自定義的 HttpBackend。

那就創(chuàng)建一個(gè)“頂層組件”來(lái)扮演該模塊中所有組件的根。 把這個(gè)自定義的 HttpBackend 提供者添加到這個(gè)頂層組件的 providers 列表中,而不是該模塊的 providers 中。 回憶一下,Angular 會(huì)為每個(gè)組件實(shí)例創(chuàng)建一個(gè)子注入器,并使用組件自己的 providers 來(lái)配置這個(gè)注入器。

當(dāng)該組件的子組件想要一個(gè) HttpBackend 服務(wù)時(shí),Angular 會(huì)提供一個(gè)局部的 HttpBackend 服務(wù),而不是應(yīng)用的根注入器創(chuàng)建的那個(gè)。 子組件將正確發(fā)起 http 請(qǐng)求,而不管其它模塊對(duì) HttpBackend 做了什么。

確保把模塊中的組件都創(chuàng)建成這個(gè)頂層組件的子組件。

你可以把這些子組件都嵌在頂層組件的模板中?;蛘?,給頂層組件一個(gè) <router-outlet>,讓它作為路由的宿主。 定義子路由,并讓路由器把模塊中的組件加載進(jìn)該路由出口(outlet)中。

雖然通過(guò)在惰性加載模塊中或組件中提供某個(gè)服務(wù)來(lái)限制它的訪問(wèn)都是可行的方式,但在組件中提供服務(wù)可能導(dǎo)致這些服務(wù)出現(xiàn)多個(gè)實(shí)例。因此,應(yīng)該優(yōu)先使用惰性加載的方式。

我應(yīng)該把全應(yīng)用級(jí)提供者添加到根模塊 AppModule 中還是根組件 AppComponent 中?

通過(guò)在服務(wù)的 @Injectable() 裝飾器中(例如服務(wù))指定 providedIn: 'root' 來(lái)定義全應(yīng)用級(jí)提供者,或者 InjectionToken 的構(gòu)造器(例如提供令牌的地方),都可以定義全應(yīng)用級(jí)提供者。 通過(guò)這種方式創(chuàng)建的服務(wù)提供者會(huì)自動(dòng)在整個(gè)應(yīng)用中可用,而不用把它列在任何模塊中。

如果某個(gè)提供者不能用這種方式配置(可能因?yàn)樗鼪](méi)有有意義的默認(rèn)值),那就在根模塊 AppModule 中注冊(cè)這些全應(yīng)用級(jí)服務(wù),而不是在 AppComponent 中。

惰性加載模塊及其組件可以注入 AppModule 中的服務(wù),卻不能注入 AppComponent 中的。

只有當(dāng)該服務(wù)必須對(duì) AppComponent 組件樹(shù)之外的組件不可見(jiàn)時(shí),才應(yīng)該把服務(wù)注冊(cè)進(jìn) AppComponentproviders 中。 這是一個(gè)非常罕見(jiàn)的異常用法。

更一般地說(shuō),優(yōu)先把提供者注冊(cè)進(jìn)模塊中,而不是組件中。

討論

Angular 把所有啟動(dòng)期模塊的提供者都注冊(cè)進(jìn)了應(yīng)用的根注入器中。 這些服務(wù)是由根注入器中的提供者創(chuàng)建的,并且在整個(gè)應(yīng)用中都可用。 它們具有應(yīng)用級(jí)作用域。

某些服務(wù)(比如 Router)只有當(dāng)注冊(cè)進(jìn)應(yīng)用的根注入器時(shí)才能正常工作。

相反,Angular 使用 AppComponent 自己的注入器注冊(cè)了 AppComponent 的提供者。 AppComponent 服務(wù)只在該組件及其子組件樹(shù)中才能使用。 它們具有組件級(jí)作用域。

AppComponent 的注入器是根注入器的子級(jí),注入器層次中的下一級(jí)。 這對(duì)于沒(méi)有路由器的應(yīng)用來(lái)說(shuō)幾乎是整個(gè)應(yīng)用了。 但對(duì)那些帶路由的應(yīng)用,路由操作位于頂層,那里不存在 AppComponent 服務(wù)。這意味著惰性加載模塊不能使用它們。

我應(yīng)該把其它提供者注冊(cè)到模塊中還是組件中?

提供者應(yīng)該使用 @Injectable 語(yǔ)法進(jìn)行配置。只要可能,就應(yīng)該把它們?cè)趹?yīng)用的根注入器中提供(providedIn: 'root')。 如果它們只被惰性加載的上下文中使用,那么這種方式配置的服務(wù)就是惰性加載的。

如果要由消費(fèi)方來(lái)決定是否把它作為全應(yīng)用級(jí)提供者,那么就要在模塊中(@NgModule.providers)注冊(cè)提供者,而不是組件中(@Component.providers)。

當(dāng)你必須把服務(wù)實(shí)例的范圍限制到某個(gè)組件及其子組件樹(shù)時(shí),就把提供者注冊(cè)到該組件中。 指令的提供者也同樣照此處理。

例如,如果英雄編輯組件需要自己私有的緩存英雄服務(wù)實(shí)例,那就應(yīng)該把 HeroService 注冊(cè)進(jìn) HeroEditorComponent 中。 這樣,每個(gè)新的 HeroEditorComponent 的實(shí)例都會(huì)得到一份自己的緩存服務(wù)實(shí)例。 編輯器的改動(dòng)只會(huì)作用于它自己的服務(wù),而不會(huì)影響到應(yīng)用中其它地方的英雄實(shí)例。

總是在根模塊 AppModule 中注冊(cè)全應(yīng)用級(jí)服務(wù),而不要在根組件 AppComponent 中。

為什么在共享模塊中為惰性加載模塊提供服務(wù)是個(gè)餿主意?

急性加載的場(chǎng)景

當(dāng)急性加載的模塊提供了服務(wù)時(shí),比如 UserService,該服務(wù)是在全應(yīng)用級(jí)可用的。如果根模塊提供了 UserService,并導(dǎo)入了另一個(gè)也提供了同一個(gè) UserService 的模塊,Angular 就會(huì)把它們中的一個(gè)注冊(cè)進(jìn)應(yīng)用的根注入器中(參見(jiàn)如果兩次導(dǎo)入了同一個(gè)模塊會(huì)怎樣?)。

然后,當(dāng)某些組件注入 UserService 時(shí),Angular 就會(huì)發(fā)現(xiàn)它已經(jīng)在應(yīng)用的根注入器中了,并交付這個(gè)全應(yīng)用級(jí)的單例服務(wù)。這樣不會(huì)出現(xiàn)問(wèn)題。

惰性加載場(chǎng)景

現(xiàn)在,考慮一個(gè)惰性加載的模塊,它也提供了一個(gè)名叫 UserService 的服務(wù)。

當(dāng)路由器準(zhǔn)備惰性加載 HeroModule 的時(shí)候,它會(huì)創(chuàng)建一個(gè)子注入器,并且把 UserService 的提供者注冊(cè)到那個(gè)子注入器中。子注入器和根注入器是不同的。

當(dāng) Angular 創(chuàng)建一個(gè)惰性加載的 HeroComponent 時(shí),它必須注入一個(gè) UserService。 這次,它會(huì)從惰性加載模塊的子注入器中查找 UserService 的提供者,并用它創(chuàng)建一個(gè) UserService 的新實(shí)例。 這個(gè) UserService 實(shí)例與 Angular 在主動(dòng)加載的組件中注入的那個(gè)全應(yīng)用級(jí)單例對(duì)象截然不同。

這個(gè)場(chǎng)景導(dǎo)致你的應(yīng)用每次都創(chuàng)建一個(gè)新的服務(wù)實(shí)例,而不是使用單例的服務(wù)。

為什么惰性加載模塊會(huì)創(chuàng)建一個(gè)子注入器?

Angular 會(huì)把 @NgModule.providers 中的提供者添加到應(yīng)用的根注入器中…… 除非該模塊是惰性加載的,這種情況下,Angular 會(huì)創(chuàng)建一子注入器,并且把該模塊的提供者添加到這個(gè)子注入器中。

這意味著模塊的行為將取決于它是在應(yīng)用啟動(dòng)期間加載的還是后來(lái)惰性加載的。如果疏忽了這一點(diǎn),可能導(dǎo)致嚴(yán)重后果。

為什么 Angular 不能像主動(dòng)加載模塊那樣把惰性加載模塊的提供者也添加到應(yīng)用程序的根注入器中呢?為什么會(huì)出現(xiàn)這種不一致?

歸根結(jié)底,這來(lái)自于 Angular 依賴注入系統(tǒng)的一個(gè)基本特征: 在注入器還沒(méi)有被第一次使用之前,可以不斷為其添加提供者。 一旦注入器已經(jīng)創(chuàng)建和開(kāi)始交付服務(wù),它的提供者列表就被凍結(jié)了,不再接受新的提供者。

當(dāng)應(yīng)用啟動(dòng)時(shí),Angular 會(huì)首先使用所有主動(dòng)加載模塊中的提供者來(lái)配置根注入器,這發(fā)生在它創(chuàng)建第一個(gè)組件以及注入任何服務(wù)之前。 一旦應(yīng)用開(kāi)始工作,應(yīng)用的根注入器就不再接受新的提供者了。

之后,應(yīng)用邏輯開(kāi)始惰性加載某個(gè)模塊。 Angular 必須把這個(gè)惰性加載模塊中的提供者添加到某個(gè)注入器中。 但是它無(wú)法將它們添加到應(yīng)用的根注入器中,因?yàn)楦⑷肫饕呀?jīng)不再接受新的提供者了。 于是,Angular 在惰性加載模塊的上下文中創(chuàng)建了一個(gè)新的子注入器。

我要如何知道一個(gè)模塊或服務(wù)是否已經(jīng)加載過(guò)了?

某些模塊及其服務(wù)只能被根模塊 AppModule 加載一次。 在惰性加載模塊中再次導(dǎo)入這個(gè)模塊會(huì)導(dǎo)致錯(cuò)誤的行為,這個(gè)錯(cuò)誤可能非常難于檢測(cè)和診斷。

為了防范這種風(fēng)險(xiǎn),可以寫(xiě)一個(gè)構(gòu)造函數(shù),它會(huì)嘗試從應(yīng)用的根注入器中注入該模塊或服務(wù)。如果這種注入成功了,那就說(shuō)明這個(gè)類是被第二次加載的,你就可以拋出一個(gè)錯(cuò)誤,或者采取其它挽救措施。

某些 NgModule(例如 BrowserModule)就實(shí)現(xiàn)了那樣一個(gè)守衛(wèi)。 下面是一個(gè)名叫 GreetingModuleNgModule 的 自定義構(gòu)造函數(shù)。

Path:"src/app/greeting/greeting.module.ts (Constructor)" 。

constructor (@Optional() @SkipSelf() parentModule?: GreetingModule) {
  if (parentModule) {
    throw new Error(
      'GreetingModule is already loaded. Import it in the AppModule only');
  }
}

什么是入口組件?

Angular 根據(jù)組件類型命令式加載的組件是入口組件.

而通過(guò)組件選擇器聲明式加載的組件則不是入口組件。

Angular 會(huì)聲明式的加載組件,它使用組件的選擇器在模板中定位元素。 然后,Angular 會(huì)創(chuàng)建該組件的 HTML 表示,并把它插入 DOM 中所選元素的內(nèi)部。它們不是入口組件。

而用于引導(dǎo)的根 AppComponent 則是一個(gè)入口組件。 雖然它的選擇器匹配了 "index.html" 中的一個(gè)元素,但是 "index.html" 并不是組件模板,而且 AppComponent 選擇器也不會(huì)在任何組件模板中出現(xiàn)。

在路由定義中用到的組件也同樣是入口組件。 路由定義根據(jù)類型來(lái)引用組件。 路由器會(huì)忽略路由組件的選擇器(即使它有選擇器),并且把該組件動(dòng)態(tài)加載到 RouterOutlet 中。

引導(dǎo)組件和入口組件有什么不同?

引導(dǎo)組件是入口組件的一種。 它是被 Angular 的引導(dǎo)(應(yīng)用啟動(dòng))過(guò)程加載到 DOM 中的入口組件。 其它入口組件則是被其它方式動(dòng)態(tài)加載的,比如被路由器加載。

@NgModule.bootstrap 屬性告訴編譯器這是一個(gè)入口組件,同時(shí)它應(yīng)該生成一些代碼來(lái)用該組件引導(dǎo)此應(yīng)用。

不需要把組件同時(shí)列在 bootstrapentryComponent 列表中 —— 雖然這樣做也沒(méi)壞處。

什么時(shí)候我應(yīng)該把組件加到 entryComponents 中?

大多數(shù)應(yīng)用開(kāi)發(fā)者都不需要把組件添加到 entryComponents 中。

Angular 會(huì)自動(dòng)把恰當(dāng)?shù)慕M件添加到入口組件中。 列在 @NgModule.bootstrap 中的組件會(huì)自動(dòng)加入。 由路由配置引用到的組件會(huì)被自動(dòng)加入。 用這兩種機(jī)制添加的組件在入口組件中占了絕大多數(shù)。

如果你的應(yīng)用要用其它手段來(lái)根據(jù)類型引導(dǎo)或動(dòng)態(tài)加載組件,那就得把它顯式添加到 entryComponents 中。

雖然把組件加到這個(gè)列表中也沒(méi)什么壞處,不過(guò)最好還是只添加真正的入口組件。 不要添加那些被其它組件的模板引用過(guò)的組件。

為什么 Angular 需要入口組件?

原因在于搖樹(shù)優(yōu)化。對(duì)于產(chǎn)品化應(yīng)用,你會(huì)希望加載盡可能小而快的代碼。 代碼中應(yīng)該僅僅包括那些實(shí)際用到的類。 它應(yīng)該排除那些從未用過(guò)的組件,無(wú)論該組件是否被聲明過(guò)。

事實(shí)上,大多數(shù)庫(kù)中聲明和導(dǎo)出的組件你都用不到。 如果你從未引用它們,那么搖樹(shù)優(yōu)化器就會(huì)從最終的代碼包中把這些組件砍掉。

如果Angular 編譯器為每個(gè)聲明的組件都生成了代碼,那么搖樹(shù)優(yōu)化器的作用就沒(méi)有了。

所以,編譯器轉(zhuǎn)而采用一種遞歸策略,它只為你用到的那些組件生成代碼。

編譯器從入口組件開(kāi)始工作,為它在入口組件的模板中找到的那些組件生成代碼,然后又為在這些組件中的模板中發(fā)現(xiàn)的組件生成代碼,以此類推。 當(dāng)這個(gè)過(guò)程結(jié)束時(shí),它就已經(jīng)為每個(gè)入口組件以及從入口組件可以抵達(dá)的每個(gè)組件生成了代碼。

如果該組件不是入口組件或者沒(méi)有在任何模板中發(fā)現(xiàn)過(guò),編譯器就會(huì)忽略它。

有哪些類型的模塊?我應(yīng)該如何使用它們?

每個(gè)應(yīng)用都不一樣。根據(jù)不同程度的經(jīng)驗(yàn),開(kāi)發(fā)者會(huì)做出不同的選擇。下列建議和指導(dǎo)原則廣受歡迎。

SharedModule

為那些可能會(huì)在應(yīng)用中到處使用的組件、指令和管道創(chuàng)建 SharedModule。 這種模塊應(yīng)該只包含 declarations,并且應(yīng)該導(dǎo)出幾乎所有 declarations 里面的聲明。

SharedModule 可以重新導(dǎo)出其它小部件模塊,比如 CommonModule、FormsModule 和提供你廣泛使用的 UI 控件的那些模塊。

SharedModule不應(yīng)該帶有 providers,原因在前面解釋過(guò)了。 它的導(dǎo)入或重新導(dǎo)出的模塊中也不應(yīng)該有 providers。 如果你要違背這條指導(dǎo)原則,請(qǐng)務(wù)必想清楚你在做什么,并要有充分的理由。

在任何特性模塊中(無(wú)論是你在應(yīng)用啟動(dòng)時(shí)主動(dòng)加載的模塊還是之后惰性加載的模塊),你都可以隨意導(dǎo)入這個(gè) SharedModule

特性模塊

特性模塊是你圍繞特定的應(yīng)用業(yè)務(wù)領(lǐng)域創(chuàng)建的模塊,比如用戶工作流、小工具集等。它們包含指定的特性,并為你的應(yīng)用提供支持,比如路由、服務(wù)、窗口部件等。 要對(duì)你的應(yīng)用中可能會(huì)有哪些特性模塊有個(gè)概念,考慮如果你要把與特定功能(比如搜索)有關(guān)的文件放進(jìn)一個(gè)目錄下,該目錄的內(nèi)容就可能是一個(gè)名叫 SearchModule 的特性模塊。 它將會(huì)包含構(gòu)成搜索功能的全部組件、路由和模板。

在 NgModule 和 JavaScript 模塊之間有什么不同?

在 Angular 應(yīng)用中,NgModule 會(huì)和 JavaScript 的模塊一起工作。

在現(xiàn)代 JavaScript 中,每個(gè)文件都是模塊(參見(jiàn)模塊)。 在每個(gè)文件中,你要寫(xiě)一個(gè) export 語(yǔ)句將模塊的一部分公開(kāi)。

Angular 模塊是一個(gè)帶有 @NgModule 裝飾器的類,而 JavaScript 模塊則沒(méi)有。 Angular 的 NgModule 有自己的 importsexports 來(lái)達(dá)到類似的目的。

你可以導(dǎo)入其它 NgModules,以便在當(dāng)前模塊的組件模板中使用它們導(dǎo)出的類。 你可以導(dǎo)出當(dāng)前 NgModules 中的類,以便其它 NgModules 可以導(dǎo)入它們,并用在自己的組件模板中。

Angular 如何查找模板中的組件、指令和管道?什么是 模板引用 ?

Angular 編譯器在組件模板內(nèi)查找其它組件、指令和管道。一旦找到了,那就是一個(gè)“模板引用”。

Angular 編譯器通過(guò)在一個(gè)模板的 HTML 中匹配組件或指令的選擇器(selector),來(lái)查找組件或指令。

編譯器通過(guò)分析模板 HTML 中的管道語(yǔ)法中是否出現(xiàn)了特定的管道名來(lái)查找對(duì)應(yīng)的管道。

Angular 只查詢兩種組件、指令或管道: 1)那些在當(dāng)前模塊中聲明過(guò)的,以及 2)那些被當(dāng)前模塊導(dǎo)入的模塊所導(dǎo)出的。

什么是 Angular 編譯器?

Angular 編譯器會(huì)把你所編寫(xiě)的應(yīng)用代碼轉(zhuǎn)換成高性能的 JavaScript 代碼。 在編譯過(guò)程中,@NgModule 的元數(shù)據(jù)扮演了很重要的角色。

你寫(xiě)的代碼是無(wú)法直接執(zhí)行的。 比如組件。 組件有一個(gè)模板,其中包含了自定義元素、屬性型指令、Angular 綁定聲明和一些顯然不屬于原生 HTML 的古怪語(yǔ)法。

Angular 編譯器讀取模板的 HTML,把它和相應(yīng)的組件類代碼組合在一起,并產(chǎn)出組件工廠。

組件工廠為組件創(chuàng)建純粹的、100% JavaScript 的表示形式,它包含了 @Component 元數(shù)據(jù)中描述的一切:HTML、綁定指令、附屬的樣式等……

由于指令和管道都出現(xiàn)在組件模板中,*Angular 編譯器**也同樣會(huì)把它們組合進(jìn)編譯后的組件代碼中。

@NgModule 元數(shù)據(jù)告訴 Angular 編譯器要為當(dāng)前模塊編譯哪些組件,以及如何把當(dāng)前模塊和其它模塊鏈接起來(lái)。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)