Figure out and divide Angular module in project _Angular 切版實戰 part 2: 如何在 Angular 專案裡切模組

前情提要 (連載的概念)
距離上次發文(切分 shared feature)已經過了半年,我們的產品持續長大,光 shared feature 底下已經擁有40多支的元件,到處 import 到處 export。於是上週開始解決技術債,整理、重新分類 module 和元件們。

在講 Angular module 的構造前,不得不說寫了幾個月 Angular 後,才發現 Angular 處境有點尷尬。雖然是模仿了 React 的 component 概念,但 React 目前生態更活躍、元件更好寫,framework 方面 AngularJS 社群比較成熟,套件多,反觀 Angular 東找西找資源真的不多 (´・ω・`)。但凡走過才有收穫,部門也有 React 和 Vue 的開發者,剛好可以互相學習,取其所長,棄其所短。

之後目標是想辦法在擁有 Angular 框架的穩定下,又能學習 React 元件相依性降到最低、資料達成單一來源,不要讓 js 資料指來指去最後變成折騰人心肝的小妖精。之前被 js 傳遞資料全部用 pointer 的特性嚇到,趕快用 immutable 壓壓驚,不過資料流還沒理出頭緒阿 _(:3」ㄥ)_

Angular 成員

首先介紹 Angular 常用的成員:module、component、directive、service。

  • module:大致可視為需執行某特定任務的功能區塊
  • component:呈現在 html 中的元件
  • directive:可彈性綁在 Component 身上的功能需求
  • service:inject 後可執行的某功能需求
    app 以 app.module 為進入點開始運行之後,依照不同的頁面需求,可藉由切換 module 或是 import 不同的 module 來決定此頁面要載入多少資源來完成工作。module 層級最高,可載入 module 、定義 component、inject service 等等。以我們專案的 HomeModule(Home 畫面所需的模組)最初的版本為例:

@NgModule({
imports: [
ROUTES,
CommonModule,
],
declarations: [
HomeComponent,
HeaderComponent,
TooltipDirective, …
],
provider: [
SwitchAuthService, …
]
exports: [
]
})
export class HomeModule {}


Module 參數

參考 Angular 文件,各 module 設定參數可粗淺說明為:

  • imports:此模組需要載入的模組資源(只有 module 能被載入)
  • declarations :此模組使用到的 component、directive
  • providers:此模組使用到的 Service(子模組也一樣可使用,providers 作用域比較廣,不像 imports 的資源只有自己本身可使用。用起來感覺很像.…..新增一支 js 檔案?很神祕還不太清楚)
  • exports:此模組輸出的資源(意即:其他模組若載入此模組,能使用的所有資源),輸出的資源必須是 imports、declarations 裡面定義過的項目。
    一開始還對 module 沒什麼概念,這初版立刻受到挑戰,在 home 的子 module — DashboardModule (dashboard 頁面)試圖加入 TooltipDirective 的時候,得到了 Angular “xxx is part of the declarations of 2 modules” 的回應。Angular 規定 一個 component 只能附屬在一個 module 底下。所以為了讓不同模組可以共用元件,我們在第二版中創了個 SharedModule,包山包海 import 一堆 module 再 export 一堆 module、component、directive,每次有新頁面要使用共用元件,就只能 import 一大包 SharedModule,造成龐大負擔。於是切分 Module 是一定要的了。

切分 Module

在第三版中,切出了 UISharedModule(放置所有 UI 相關功能的模組):

@NgModule({
imports: [
CommonModule,
],
declarations: [
TooltipDirective,
PlaceholderComponent, …
],
provider: [
dialogService,
]
exports: [
TooltipDirective,
PlaceholderComponent, …
]
})
export class UISharedModule {}


還有 DataPipeModule、LayoutSharedModule 等等其他功能性的模組區塊,並整理出以下特性:

1. component / directive / pipe 等元件只能定義在一個 module 底下,因此只要有通用的元件,就要分類到某個 module 麾下,才能被多個 module 給 import 使用
2. exports 僅含 declaration 裡面的元件,儘量不要 export module,避免 module相依性、減輕 module 重量
3. Module 分越細,每個新頁面負擔越小,不會單一 import 就很重,但需要import的項目數量也會更多,需自行拿捏
其中第二點提到的 “不要 export module” 是因為避免在 import module 的時候,載入了非預期的 module。譬如之前因為 TagModule 與 TagSharedModule 常常被一起使用,初版長這樣:

@NgModule({
imports: [
TagSharedModule,
],
declarations: [
TagComponent,
],
exports: [
TagComponent,
TagSharedModule, …
]
})
export class TagModule {}


原本是方便載入 TagModule 時可以連 TagSharedModule 一起使用。但也會導致原本只想使用 TagComponent 的 module 被迫載入 TagSharedModule。所以即使兩者常常被一起使用,TagModule 也還是不要 exports TagSharedModule 才好,需要兩者的時候再分別 imports:

@NgModule({

imports: [
TagModule,
TagSharedModule, …
]
})
export class SomeModule {}


原本只是想寫一下上週辛苦的切分 module 甘苦談,結果居然變成很正經的分享了 (*´・д・)? 不得不說 Angaulr 這種分層架構,對一切講求扁平的 component 世代很不利,但對團隊開發與理解是有幫助的。只希望之後優化專案的過程,能找到更好的結合兩者好處的開發方法。

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *