當心 Angular 中的單例 Service

你可能知道,當咱們經過@NgModule()裝飾器來聲明一個service時,它將符合單例模式,同時還意味着它與整個應用的生命週期保持一致。好比:git

export class AdminService {
  data = Array(10000).fill(dummy);
}
@NgModule({
  providers: [AdminService, AdminDataService]
})

咱們在剛開始接觸Angular的時候,老是不計後果的將全部service都使用@NgModule()來聲明,這將會形成一個不易發現的問題:github

You are not releasing memory.

在上面的例子中,儘管你再也不須要這些內存中儲存的數據,可是讓咱們停下來仔細想想,咱們真的須要將一個service聲明爲單例的嗎?緩存

好比,在咱們整個應用中,咱們會有一個管理區域須要呈現大量的表格數據(同時這些數據只在這個管理區域展示),這些數據會儲存在內存中。在這種狀況下,咱們沒有必要將這個service聲明爲單例的,由於咱們不須要緩衝層來緩存這些數據以供應用中的其餘模塊使用。ide

進一步講,當前咱們僅僅是想使這些表格數據在多個component之間共享,同時將數據與service中的多個helper方法耦合起來。因此咱們徹底能夠直接使用@Component()裝飾器來聲明service,這樣它就會成爲一個非單例service,以下:函數

@Component({
  selector: 'admin-tab',
  providers: [AdminService, AdminDataService]
})

這樣作的好處是,當Angular註銷組件實例時,Angular將同時註銷與之綁定的service實例,y也會釋放那些用來儲存數據的內存。工具

OnDestroy 鉤子函數

許多開發者也許不知道非單例servicengOnDestroy()生命週期,因此你也能夠在這個生命週期中進行一些銷燬邏輯代碼的編寫,好比:ui

export class AdminService implements OnDestroy {
  ngOnDestroy() {
    // Clean subscriptions, intervals, etc
  }  
}

另外,若是咱們調用NgModuleRef.destroy()或者PlatformRef.destroy(),單例servicengOnDestroy鉤子函數也會被[執行]。(https://github.com/angular/an...翻譯

譯者注

之因此翻譯了這篇文章,是由於今天在整理項目代碼的時候,偶然發現了這個問題,雖然我使用Angular也有一段時間了,可是依然將不少沒有必要聲明在NgModule中的服務以單例模式的方式聲明瞭。文章中指出的問題確實是一個重要但又難以發現的問題。3d

大致總結一下Angular中聲明service的不一樣方式和應用場景。code

使用@Component

這時service與組件自己生命週期保持一致,非單例,適合聲明一些須要暫存數據的工具類或者僅在某個或某幾個組件中須要緩存數據的狀態管理類service

使用@NgModuleproviders

這時service與應用自己生命週期保持一致(非懶加載),單例,適合聲明一些須要在全局緩存數據的狀態管理類service

可是有一個特例,懶加載模塊中的service是會在模塊加載時從新建立一個實例的,懶加載模塊中均會注入後建立的service實例,所以懶加載模塊與非懶加載模塊間的service非單例。

使用forRoot

使用forRoot能夠保證當前模塊即便是懶加載模塊,在加載時也不會從新建立一個新的service實例,由於懶加載模塊在加載時,會臨時建立一個從屬於根injector的子injector,根據Angular中的依賴注入流程,當嘗試經過一個子injector中注入不存在的實例對象時,會嘗試向父級injector獲取,所以最終可保證該service在應用任何地方被注入均是單例。

關於官方文檔的介紹,能夠參考ProvidersSingleton Services

相關文章
相關標籤/搜索