龔承仕
11 min readMar 1, 2020

淺談Angular的模組化

會寫這篇文章的原因主要是給自己的一些複習, 讓自己以及讀者更了解Angular模組化的概念

目錄:

  1. NgModule的定義
  2. 為什麼要進行模組化?
  3. 何謂LazyLoading
  4. Lazy-Loading code展示

就讓我們開始吧!

NgModule的定義

一個被@NgModule decorator所裝飾的class

一個@NgModule decorator有四個重要的property

  1. declarations: 在裡面的宣告了這個module自己的components, directives, pipes….而這些是你想要在你的template中所使用的東西

2. imports: 宣告這個模組所要使用的其他模組.

例如這個module現在需要使用ReactiveFormsModule就必須把ReactiveFormsModule寫入這個模組的imports:[] array中.

3. exports: 把自己所屬的東西傳給其他的component使用

4. providers: 新增Service provider給Angular的dependency injector, 要注意的是在不同的模組之間所引入的Service provider可能會導致Angular創造出多個Service instances.(下一篇會講到在不同模組中的providers所宣告的Service會有不同的Scope)

以下的這張圖為基本的NgModule decorator相信如果你有寫Angular一定有看過

basic NgModule decorator and its properties

為什麼要進行模組化 ?

在模組化之前, 所有的Component, directives, pipe, 需要的其他module(例如: ReactiveFormsModule)是要被全部寫入app.module中的, 這會導app.module變得非常雜亂, 以下是我認為模組化是必要的原因

1.(可以想像一下當一個大型網站擁有幾百個components, directives後一同塞入app.module的情型), 到後來如果我們想要維護這份程式的時候就會變得特別難維護,.

2. 另外模組化的另個原因則是要讓某些Component達到lazy-loading的效果

何謂lazy-loading?

在所有你在app.module所加入的component, directives, pipes都會進行所謂eager-loading的動作.

Eager-Loading → 顧名思義就是不管你需不需要這個Compnent, 你在打開網站一開始就會載入所有的Component, 而今天如果你是一個大型網站的話則會導致你在一開始所載入的bundle size過大導致載入速度變慢.

Lazy-Loading → 在你需要這個module的時候, 你才去載入他, 要記住的是, 如果你想要一個component是lazy-loaded的話, 你就不行把這個component宣告在app module的declarations array中, 放在app.module declarations array中的component全部都是eager-loading的

Lazy-Loading 實際範例

Link: https://github.com/johnny0221/Lazy-Loading-Example

以下的範例來自於上面的repository, 有興趣可以自己下載來玩玩

在這項範例中

我會把一個component設置成eager-loading, 另一個設置成lazy-loading來進行比較

由於lazy-loading通常是搭配Angular Routing(在不同頁面載入不同的module)來進行配合, 所以也在此程式碼中建立了簡單的routing功能.

1. 結構解說

創立了三個Component分別為

  1. eager-loaded component
  2. home component
  3. lazy-loaded component
  1. 屬於Eager Loading的Components: home, eager-loaded

一般來說你用以下敘述新增的component, 都會自動被載入到app.module中形成eager loading component.

ng generate component <name>

在app.module file中, 我們可以看到HomeComponent和EagerLoadedCompoent被宣告在declarations array中, 沒什麼特別的, 這也就是你創立一個Angular component 預設的情形

2. Lazy Loading Component / Module: 其實通常我們都會去LazyLoad一個module並不是一個component, 在這裡你可以把一個頁面想像成一個module來看待, 因為在現實我們想要lazy-load不只是一個component….(一個頁面可能包含數個components)

例如: 我創立一個電商網站, 我想要lazy-load我的登入頁面, 或著我想要lazy-load我的商品頁面等等…

不同於其他兩個component, 我們在這個folder底下多了一個component本身的module和routing module,

lazy-loaded.module.ts(下圖)

再來複習一下, 在宣告一個模組就好比在宣告我在這個模組中

需要動用到哪些Component, directive, pipe → declarations: []

哪些其他額外的模組, 例如ReactiveFormsModule? → imports: []

又或著是我個模組是一個給別人用的模組? → exports: []

而在此範例中, 我們在imports array中宣告了我們的另一個file中匯出的module, LazyLoadingRoutingModule.

你現在只要知道LazyLoadingRoutingModule是LazyLoadedModule執行時需要的一個模組就好

再說LazyLoadingRoutingModule.ts之前, 先來說說app.routing.module.ts中裝了甚麼,

2. Lazy-Loading 路由宣告概念

app.module.ts(下圖)

在這個routing module中我們首先定義了兩個eager-loading component的路由, 在這邊就不在多做介紹, 我們要看的是如何幫一個lazy-loaded的模組來定義它的路由, 那就是要幫lazy-loading 的route加上一個loadChildren的內容

你可以把loadChildren想成一個告訴Angular說

Hey Angular, 在使用者到/lazy中時, 你在載入lazy-loaded module喔!

那這時問題來了, 你要在哪宣告你在/lazy中要顯示的component呢?

而就是在你要進行Lazy-Loading的那個Module中!

提醒(Note)

在上述圖片中有兩種宣告loadChildren的寫法(紅色叉勾標注的地方)

你可能會在某些地方看過註解內容的寫法

註解內容為lazy-loading的舊版寫法

而實際內容則為新的寫法(dynamic import)

3. 定義/lazy中所對應的Component

Lazy-loaded.routing.module.ts(下圖)

在LazyLoadingRoutingModule中定義的routes對應的Component.

你可能會覺得為什麼path對應的是空字串, 我們不是要定義對應/lazy之後的route嗎?

這裡的path會以你在app.routing.module裡面定義的path(也就是/lazy)為基準

根據上面我們在app.routing.module中定義的lazy-loading route為/lazy, 而我們在這個file中所定義的path則會添加到/lazy後面,

例如上面path: ' '會代表在http://<domain name>/lazy,

假如為path: ' example' 則是在http://<domain name>/lazy/example所顯示的內容以此類推….

4. 結果 & 如何確認lazy-loading成功執行了?

ng serve後, 會出現以下的內容,

點擊eager button時會進去/eager中, 而該route則會給你EagerLoadedComponent(這是component的名字)為一個eager-loading的component. (有點繞口不好意思),

點擊lazy button時會進入/lazy中, 而該route則會給你LazyLoadedComponent(這是component的名字)唯一個lazy-loading的component

我們有兩種方法可以確認你寫的lazy-loading有沒有作用

1. Chrome dev tool 的 network tab

我們來比對在兩個route, /eager和/lazy中我們network tab是怎麼變動的

  1. /eager

進入/eager route時的 network tab中顯示的就和平常沒兩樣(不知道每個人一不一樣就是了)

2. /lazy

在進入/lazy route時我們可以明顯的看到紅色底線的那個request, 也就是說我們的模組是在進入到/lazy後才被載入的! 這樣就成功地完成了lazy-loading了

2. Augury

Augury是一個Chrome Extension tool, 在大型網站中路由總是複雜的, 而Augury則提供了我們一個非常好的樹狀圖來讓我們觀看樹狀路由

首先你可以先去下載Augury, 等下載成功後會再Chorme dev tool bar上顯示

再來點進去後, 我們選擇router-Tree的選項就可以看見我們的路由結構了

而在我們也能看到那個component也確實顯示為lazy-loading.

結語

在這篇文章中講述了Angular模組化的其中一個優點Lazy-Loading, 我的文筆其實非常之差, 在某些地方如有錯誤, 也拜託各位提醒我, 希望這篇文章有幫助到觀看者.

在下一篇中我打算講講關於Service Scope相關的概念, 有時候你想要你創造的Service所服務的對象僅限於某些地方要怎麼寫以及你必須維持一個Singleton Service都是在Angular中重要的環節

現在也還在拜讀Angular的docMax大大的文章https://indepth.dev/avoiding-common-confusions-with-modules-in-angular/

等研究完後我們再見!