Angular 應用開發裡使用 ForRoot 解決 Lazy Loaded Module 裡單例行為丟失的問題

JerryWang_汪子熙發表於2022-12-10

筆者在 Angular 實際專案開發中曾經遇到這樣一個需求:

我們想建立一個共享模組,它將包含一個配置來設定布林值(作為標誌)以啟用或禁用其他模組的某些功能。 其他模組可以在 Angular 應用程式的引導期間載入,也可以是延遲載入的模組。

ForRoot 的使用場景

當我們想要跨應用程式維護服務的單個例項(單例)時使用,這些應用程式也將具有延遲載入的模組。

舉個例子,看看託管在 StackBlitz 上這個演示程式碼,其中計數器對於急切和延遲載入模組的行為不同。

sharedModule 的 provider 陣列裡匯入了這個服務:

在此示例中,我們共享一項服務以跟上計數器值。 每次任何元件增加儲存在計數器服務中的值時,我都想與所有元件共享它。

我在 app.routes.ts 裡定義了一個路由陣列 routing

上圖透過 LazyLoad 的方式,載入 LazyModule,後者的實現:

LazyModule 匯入 SharedModule,是為了使用其計數器 CounterService:

問題是當我們嘗試引入延遲載入模組時。 請注意延遲載入的元件如何不共享相同的計數器值。 當僅使用預載入元件時,如果您使用共享服務,下面的示例將起作用,但請注意延遲載入元件的行為方式。惰性元件獲取自己的服務例項。

但這個解決方案的問題是:我們在 Eager Load 的 Component 裡點選 Counter 按鈕,增加計數器的值後,點選 Lazy 超連結,進入 LazyModule 裡的 Component,我們期望此時在 Component 裡顯示的值也為 7:

然而事與願違,Lazy Component 裡的值為 0:

計數器由駐留在 SharedModule 下的 CounterService 維護。 由於延遲載入的模組建立了自己的服務例項,我們失去了 Angular 服務的單例行為。

為了解決這個問題,我們需要引入 forRoot() 的概念。 可以在這個演示中看到工作示例。這是同樣的原因,我們將它與 RouterModule 一起使用,以幫助 RouterService 瞭解具有多個模組的應用程式行為。

RouterModule.forRoot(ROUTES)

修改後的解決方案,SharedModule 的實現程式碼:

import { NgModule, ModuleWithProviders } from '@angular/core';
import { CounterService } from './counter.service';

@NgModule({

})
export class SharedModule {
    static forRoot(): ModuleWithProviders {
    return {
      ngModule: SharedModule,
      providers: [ CounterService ]
    }
  }
}

App module 裡呼叫這個方法:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { SharedModule } from './shared/shared.module';

import { AppComponent } from './app.component';
import { EagerComponent } from './eager.component';
import { routing } from './app.routes';

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

修改之後的程式碼連結

之後可以在 Eager Load 和 Lazy Load Component 之間任意切換,單例模式的行為能夠正常工作。

參考文獻

相關文章