[聚合文章] 使用 forRoot() 幫助 SharedModule 提供單一實例服務

Angular 2017-12-14 16 阅读

在 Angular 專案中,我們會把共用的元件(Component)、指令(Directive)、管道(Pipe)放在一個 SharedModule 中做管理,那服務(Service)呢?一般來說,我們希望服務是單一實例(Singleton Service)的狀態,但基於底層運作的方式,直接在 SharedModule 建立服務,會產生多個重複的單一實例服務,而可能引發問題,這時我們可以透過 forRoot() 來幫我們避免這狀況的發生。

模組分割

Angular 提供模組化的概念讓我們能輕鬆管理應用程式的各個功能,在官方的 Style Guide 針對模組架構也做了相當多的建議。

根據 Style Guide 的模組章節 ,會建議我們會將 Angular 專案分成以下幾個模組:

  • 共享模組 (SharedModule)
  • 核心模組 (CoreModule)
  • 特性模組 (FeatureModule)

特性模組不是只做一個模組叫 FeatureModule ,而且將多個功能特性區分成多個模組,例如 AFeatureModuleBFeatureModule

在分割模組的時候有一點要注意,SharedModule 經常會匯入到各個子模組中,應該盡量避免在 SharedModule 中建立 App 層級的服務,因為這樣會讓每個子模組都產生自己的單一實例服務(Singleton Service), 造成 App 中有多個一樣的單一實例服務 ,而可能引發問題(如果你是刻意要這樣做,也是可以啦)。

提供單一實例服務

要避免 SharedModule 被多個子模組匯入時,產生多個單一實例服務,可以在 SharedModule 中定義一個返回 ModuleWithProviders 對象的靜態 forRoot() 方法。

ModuleWithProviders 對象是一個介面(Interface), 程式碼 定義內容如下:

// A wrapper around a module that also includes the providers.
export interface ModuleWithProviders {
  ngModule: Type<any>;		// 模組名稱
  providers?: Provider[];	// 要註冊的服務
}

接下來看看 SharedModuleAppModule 分別要怎麼寫。

SharedModule

我們要將 App 層級的單一實例服務放在 SharedModule 中時,可以這樣寫:

import { NgModule, ModuleWithProviders } from '@angular/core';
import { MyService } from './my.service';

@NgModule({
  ...
})
export class SharedModule {
  static forRoot(): ModuleWithProviders {
    return {
      ngModule: SharedModule,
      providers: [ MyService ]
    };
  }
}

在 SharedModule 的 ngModule 照原本的方式做宣告( declarations )、匯入( imports )、匯出( exports )的動作,但不處理提供服務( providers ),而在裡面定義一個靜態的 forRoot() 方法,這方法會返回 ModuleWithProviders 對象並包含要提供的單一實例服務。

forRoot() 方法是什麼?這是一個約定的用法,你可以把他視為一個只提供給根模組做配置的設定方法,因此我們也只能在根模組中使用 forRoot() 。此外還有一個 forChild() 約定用法,給子模組做配置。

AppModule

在根模組 AppModule 中,我們可以匯入 SharedModule 並呼叫 forRoot() 靜態方法,藉此註冊 SharedModule 要提供的服務。

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 {}

你可能曾經看過 RouterModule 也用了同樣的作法,不過他要做的是將路由設定導入給根模組。

子模組

透過上面的設定方式,子模組可以安心的匯入 SharedModule 並使用所提供的元件、指令、管道等功能,而不會因此產生另外一個服務實例。

這個技巧讓整個 Angular 專案架構更容易被管理,也使的共用模組(Shared Module)或延遲載入模組(Lazy-Loaded Module)的行為更容易被預測。

參考資料:

注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。