NgModule 是你使用 Angular 編寫應用程序時遇到的第一個基本結構,但因爲涉及不一樣的做用域範圍,它也是最微妙和最複雜的。若是你想詳細瞭解 NgModule 的相關知識,能夠直接參考 Angualr NgModule FAQ 或 Angular FAQ 文章中 NgModule 版塊的內容 。html
咱們可使用 Angular CLI,自動完成不少工做,但咱們必需要作的第一件事就是加載根模塊。git
platformBrowserDynamic().bootstrapModule(AppModule);
NgModule 的目的是聲明咱們在 Angular 模塊中建立的內容,主要有如下兩種結構:github
declarations - 聲明模板中使用的內容,大部分是組件,也包括指令和管道。typescript
providers - 用於聲明服務。bootstrap
import { NgModule } from '@angular/core'; import { SomeComponent } from './some.component'; import { SomeDirective } from './some.directive'; import { SomePipe } from './some.pipe'; import { SomeService } from './shared/some.service'; @NgModule({ declarations: [SomeComponent, SomeDirective, SomePipe], providers: [SomeService] }) export class SomeModule {}
NgModule 是 Angular RC 階段新增的一個功能,由於 ES 6 中已經新增 import/export 功能,在引入 NgModule 彷佛增長了沒必要要的複雜性。但引入了 NgModule 讓咱們可使用 AOT
編譯,這大大提升了應用的性能。在 Angular 2 早期 Beta 版本中,咱們的組件在使用指令時,都必須每次導入相應的指令,這大大增長開發成本。框架
declarations
和 providers
屬性最使人困惑的是,它們沒有相同的做用域和可見性 (scope / visibility):ide
declarations / components 是本地做用域 (private visibility)性能
providers / services 是全局做用域 (public visibility)flex
這意味着你聲明的組件只能在當前模塊中使用。若是你想要在外面使用聲明的組件,你必須導出它們:code
import { NgModule } from '@angular/core'; import { SomeComponent } from './some.component'; import { SomeDirective } from './some.directive'; import { SomePipe } from './some.pipe'; @NgModule({ declarations: [SomeComponent, SomeDirective, SomePipe], exports: [SomeComponent, SomeDirective, SomePipe] }) export class SomeModule {}
反之,模塊中的聲明的服務,咱們能夠在全部模塊中使用。
components 和 services 擁有不一樣的做用域,瞭解這個區別很重要。若是咱們的應用程序不只僅包含一個模塊,事情可能會變得很糟糕。Angular 框架內部也拆分紅多個不一樣的模塊,如 core、common、http 等等。
在 Angular 模塊中咱們須要作的一件主要的事情是導入其它模塊:
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { HttpModule } from '@angular/http'; import { FeatureModule } from '../feature/feature.module'; @NgModule({ imports: [CommonModule, HttpModule, FeatureModule] }) export class SomeModule {}
如今的主要問題是,你必須知道爲何須要導入這些模塊:
爲了使用導入模塊中聲明的組件、指令或管道?
爲了使用模塊中定義的服務?
由於組件和服務,擁有不一樣的做用域:
若是咱們須要在模塊中使用導入模塊中聲明的組件,那咱們須要在每一個使用的模塊中導入對應的模塊
若是咱們只是使用模塊中定義的服務,那咱們只須要在主模塊中導入對應的模塊
若是你不瞭解這些區別,你可能因爲忘記導入某個模塊,而出現組件不可用的錯誤。或者你爲了使用某個模塊中定義的服務,而屢次導入同一個模塊。
CommonModule (包含 Angular 中定義的內建指令,如 ngIf、ngFor 等),除了在主模塊以外,不須要導入,由於咱們已經在主模塊中導入了 BrowserModule
(此模塊已導入了 CommonModule)。其它模塊都必須手動導入該模塊。
FormsModule / ReactiveFormsModule
BrowserAnimationsModule
FlexLayoutModule
MaterialModule 和 UI Modules (如 PrimeNg)
Other Modules (定義 components、directives 或 pipes)
HttpModule
Other Modules (僅提供服務)
若使用 Angular CLI 建立新的模塊,它會自動幫咱們導入 CommonModule
。若是你須要使用與組件相關的模塊如 animations 、flex layout 或 Material 模塊,你必須每次手動導入上述模塊。所以一個好的實踐是建立 SharedModule 模塊。
管理 SharedModule 時,須要很當心。若是你只是導入模塊,它將沒法正常工做:
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FlexLayoutModule } from '@angular/flex-layout'; import { MaterialModule } from '@angular/material'; @NgModule({ imports: [CommonModule, FlexLayoutModule, MaterialModule] }) export class SharedModule {}
爲何呢?緣由還是做用域的問題:組件只在 SharedModule 中可用,在其它導入 SharedModule 的模塊中,仍然是不可用的。所以咱們須要導出相應的組件:
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FlexLayoutModule } from '@angular/flex-layout'; import { MaterialModule } from '@angular/material'; @NgModule({ imports: [CommonModule, FlexLayoutModule, MaterialModule], exports: [CommonModule, FlexLayoutModule, MaterialModule] }) export class SharedModule {}
若是咱們組件不包含其它共享的資源,咱們能夠省略 imports 屬性:
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FlexLayoutModule } from '@angular/flex-layout'; import { MaterialModule } from '@angular/material'; @NgModule({ exports: [CommonModule, FlexLayoutModule, MaterialModule] }) export class SharedModule {}
如何同時管理具備組件和服務的模塊?這是一個比較複雜的問題。你可能已經接觸過 RouterModule,該模塊不只提供了 <router-outlet>
、routerLink
指令,並且還提供了 ActivedRouter
服務 (用於獲取 URL 參數)、Router
服務 (用於頁面導航) 。
幸運的是,這個問題是由模塊自己來解決。 Angular CLI 會爲咱們自動生成路由文件,但你可能已經注意到,應用程序主模塊的路由和子模塊路由之間存在細微差異。
對於 AppModule,咱們這樣使用:
RouterModule.forRoot(routes)
對於子模塊,咱們這樣使用:
RouterModule.forChild(routes)
爲何呢?由於在 AppModule 中,forRoot() 方法會導入路由模塊中的指令和服務。但對於子模塊來講,forChild() 方法僅會導入路由模塊中定義的指令,而不會再次導入模塊中定義的服務。
若是你須要實現模塊懶加載,能夠簡單地使用 Angular CLI 來實現:
const routes: Routes = [ { path: 'admin', loadChildren: './admin/admin.module#AdminModule' } ];
由於它將是一個不一樣的包和模塊,默認狀況下是按需加載,它不會包含在您的應用程序的全局範圍內。
對於組件來講,你不須要更改任何內容:就像在任何子模塊中同樣,你須要再次導入 CommonModule 或 SharedModule 模塊。
但對於服務來講,有一些區別:
你仍然能夠訪問應用程序中提供的服務 (如Http和已定義的服務)。
延遲加載模塊中定義的服務,只能在延遲加載的模塊中使用,應用程序的其它模塊是沒法使用的。