Angular模塊與依賴注入

Angular模塊劃分和依賴注入的思想能夠說是Angular架構核心中的核心,最近通讀了一遍Angular官網上的模塊和依賴注入兩章,也領會到了其中的一些精髓。bootstrap

Angular的模塊NgModule,其做用其實有點相似於Jave的package和C#的namespace,是代碼組織和代碼分割的一種形式。下面是Angular官網上的一段經典的NgModule的寫法:數組

@NgModule({
  declarations: [
    AppComponent,
    ItemDirective
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

能夠看出,NgModule其實是在一個普通的class上加上一些描述性的元數據,這些元數據的意義是:架構

  • declarations: 聲明某些組件、指令和管道屬於這個模塊。
  • exports: 公開其中的部分組件、指令和管道,以便其它模塊中的組件模板中可使用它們。
  • imports: 導入其它帶有組件、指令和管道的模塊,這些模塊中的元件都是本模塊所需的。
  • providers: 提供一些供應用中的其它組件使用的服務。

1. 模塊間分享組件

NgModule經過declares來申明瞭一些組件(component)和類組件(如directivepipe,下文統稱爲組件)是隸屬於本身模塊的,並且這些組件和類組件必須且僅僅只能屬於一個NgModule,就好像每一個人都必須是屬於某一個國家。
當你要用到其餘NgModule的組件,就須要import其餘的模塊了。可是這裏注意,import的模塊不是全部的組件都是可使用的,import模塊必須明確代表了它的哪些組件是能夠外用的,這由exports數組來定義。三者的關係見下圖:app

clipboard.png

2. 模塊間分享服務

NgModule的元數據中還有一個屬性叫providers,這個屬性裏通常是聲明瞭一些service。通常狀況下咱們是但願這些service在模塊內的組件中共享的,Angular確實也是如此設計的,由於providers中申明的services都是單例模式的。
可是若是咱們要用到其餘模塊的服務,是否也是像模塊間共享組件同樣,經過import把模塊引入進來就能夠呢?確實如此,可是咱們還須要理解得更深入一些。
在Angular的設計思想裏,組件是私有化的,服務是公有化的。你能夠這麼來理解,把組件看成「人才」,把服務看成「知識」,知識是無國界的,能夠共享,可是人才是有國界的,不能隨便共享(只有exports出去的才能夠共享)。當外來模塊被import進來後,它的服務就被共享到你的模塊中了。
咱們知道,每一個NgModule都有injector,裏面存放着經過providers聲明過的service。若是經過imports導入了外來模塊,那麼外來模塊的服務就都注入到了你所在模塊的injectors中,以下圖所示。ide

clipboard.png

3. 懶加載下的服務共享

若是你的app中應用到了懶加載,那麼狀況就會更加複雜了。由於懶加載模塊是在特定的狀況下app須要用到的時候纔會被加載進來,因此通常狀況下懶加載模塊下的serivce不會被imports到主模塊中的,也就不會注入到root injector的,而是在root injector下從新開闢了一個child injector。若是你在主模塊和懶加載模塊都provide了一樣的service,那麼就會在兩個模塊中分別擁有不一樣的實例。而這每每是開發者不想看到的,由於服務的做用就是共享數據,而此時不知不覺有兩個實例存在,每一個實例單獨維護一份數據,那麼就會形成邏輯上的錯誤,更可怕的是,不少開發者並不知道這一點。因此記得在懶加載模塊中不要注入跟主模塊相同的服務,用主模塊中的就行了。可是有時候你由於要用到某些特殊指令,又不得不導入相同的模塊,好比路由模塊,在主模塊和懶加載模塊甚至是一些特徵模塊中都須要導入,這個時候,就須要在主模塊中用到forRoot了。那麼forRoot到底起到一個什麼做用呢?其實他們只是一個Angular模塊中約定俗成的寫法,主要是它們有一個返回類型ModuleWithProviders,其實就是想把moduleproviders給區分開來,它的意思就是告訴導入模塊能夠共享被導入模塊中的組件(被exports出的組件),可是服務注入一次就行了,之後要是再有一樣的服務須要注入就忽略之,不要建立兩個不一樣實例的服務。
懶加載模塊代碼以下:學習

import { NgModule, ModuleWithProviders } from '@angular/core';

import { MyDirective } from './my.directive';
import { FunPipe } from './fun.pipe';
import { SomeService } from './some.service';

@NgModule({
  declarations: [
      FunPipe,
      MyDirective
  ],
  exports: [
      FunPipe,
      MyDirective
  ]
})
export class SharedModule {
    static forRoot(): ModuleWithProviders {
        return {
            ngModule:SharedModule,
            providers:[ SomeService ]
        };
    }
}

咱們在NgModule的元數據中像往常同樣聲明和導出咱們的管道和指令,可是咱們不提供服務,而是在模塊的類中定義一個靜態方法forRoot,該方法返回一個實現Angular的 ModuleWithProviders 接口的對象。
在咱們的應用模塊中,導入懶加載模塊並調用forRoot靜態方法來提供咱們的服務:spa

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { SharedModule } from './shared/shared.module';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    SharedModule.forRoot()
  ],
  bootstrap: [
    AppComponent
  ]
})
export class AppModule {}

這樣,在導入模塊和被導入的懶加載模塊中僅僅維護了一個service實例。設計

4. 服務在組件中私有化

在Angular的@Component裏也有個providers,可是若是你在組件級別注入了service,那麼這個service就只能在該組件和它的子組件中使用了,別的組件即便在同一個模塊中也不能使用這個service。組件這個時候相似於一個家庭,這個私有的服務其實相似於傳家寶,這個傳家寶只能在大家這個家庭裏傳下去,它有很強的排他性,並且每一個組件實例都會獨享一份service,這能夠保證這個service只服務於你這個組件實例,是專屬於你本身的「私人銀行」。好比有不少個申請表,雖然申請表都是同樣的,每一個服務都必須服務於本身的申請表,這種狀況下就適合於用到component級別的provide。code

總結

若是想系統的學習Angular的模塊和依賴注入知識,建議去Angular官網好好研讀。我這裏只是學習事後加入了本身的理解,又因爲我最近在作關於分包加載涉及到Angular Module和DI比較多,也比較有體會,遂成此拙文。component

相關文章
相關標籤/搜索