試したこと
環境
- Angular:5.2.6
- Angular-CLI:1.7.1
モジュール構成の戦略
AppModuleをAppShellモデルにする
AppShellモデルのために、AppModuleは以下の役目だけを持たせます。
- application shell
- 各content(LazyLoadingModule)へのルーティング
- DIコンテナの最上位ルート
これにより初回ロードに必要なmain.bundleは、AppShellだけになります。
アプリ全体だと次のようになります。
各contentはLazyLoadingModuleへ回して、できるだけ共有リソースもAppModuleから外すことで、ユーザー作成ソースだけでなくベンダー系ソースも後送りにします。結果を見ると、ベンダー系ソースではAngularMaterialの効果が絶大です。
以上の変更によりmain.bundleがサイズ削減されて起動時間が短縮します。
元の構成
- 共有リソースをSharedModuleに全て突っ込んでいた
- SharedModuleをAppModule含めたあらゆるモジュールでインポートする
- 最上位のAppModuleで
forRoot()
によるシングルトンサービスを生成する - Rxは
rxjs/add/
からインポート
新しい構成
- SharedModuleとCoreModuleに分ける
- AppModuleにCoreModuleをインポートする
- 各LazyLoadingModuleにSharedModuleをインポートする
- Rxをpipe化する
CoreModuleとSharedModuleの分け方
CoreModule
- ナビゲーション系component
- ナビゲーション系componentで使うdirective/pipe、materialモジュール
- アプリ全体で使用するAngularパッケージ
- BrowserAnimationsModule
- HttpClientModule
- ServiceWorkerModule
- アプリ全体で使用するservice
- app-routingで使用するguard
SharedModule
- フィーチャーモジュールで使用するAngularパッケージ
- CommonModule
- FormsModule/ReactiveFormsModule
- フィーチャーモジュールで使用する共有リソース
- directive
- pipe
- guard
- materialモジュール
共有serviceは全てCoreModuleに入れています。特定のフィーチャーモジュールだけで使うserviceなら特定のフィーチャーモジュールでprovideします(SharedModuleには入れません)。複数モジュールに跨るservideをSharedModuleに入れてしまうと、フィーチャーモジュール毎にコピーを生成します。そうなるとアプリ全体で見た時にシングルトンのサービスにならないため、データの共有できないことに注意です。
バンドルファイルのサイズ変化
サマリー
- 後送りにした共有リソース分がmain.bundleからcommon.chunkに移動する。
-
--prod
ビルドのmain.bundleは、--aot
ビルドでのmain.bundleとvendor.bundleで構成されるが、--aot
ビルドでは以下の変化となる。- SharedModuleに回したユーザー作成リソースがmain.bundleからcommon.chunkに移動する。
- Angularパッケージ系(FormsModuleやAngularMaterial)のソースがvendor.bundleからcommon.chunkに移動する。
- RxJSはvendor.bundleのままで移動しない。これはvendor.bundle内のAngularパッケージが使用しているからだと思われる。
- 微量だが、合計サイズも減った。要因は不明である。
集計方法
- 採取用のサンプルソースを作成した。
- 元の状態はコミット
refactor: divide feature module from app module
3である - 新しい状態はコミット
refactor: reconstruct module and reorganize rxjs
4である - フィーチャーモジュールが一つのためcommon.chunkは作成されない。demo.chunkがsharedModuleを内包している。
- 上記2つのコミットに対して、
--aot
ビルドと--prod
ビルドでそれぞれビルド結果を採取した。
--aot
ビルドでの変化
元の構成でのビルド結果
Time: 12854ms
chunk {demo.module} demo.module.chunk.js, demo.module.chunk.js.map () 51.4 kB [rendered]
chunk {inline} inline.bundle.js, inline.bundle.js.map (inline) 5.83 kB [entry] [rendered]
chunk {main} main.bundle.js, main.bundle.js.map (main) 61.4 kB [initial] [rendered]
chunk {polyfills} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 325 kB [initial] [rendered]
chunk {styles} styles.bundle.js, styles.bundle.js.map (styles) 63.7 kB [initial] [rendered]
chunk {vendor} vendor.bundle.js, vendor.bundle.js.map (vendor) 3.88 MB [initial] [rendered]
新しい構成でのビルド結果
Time: 12936ms
chunk {demo.module} demo.module.chunk.js, demo.module.chunk.js.map () 2.04 MB [rendered]
chunk {inline} inline.bundle.js, inline.bundle.js.map (inline) 5.83 kB [entry] [rendered]
chunk {main} main.bundle.js, main.bundle.js.map (main) 30.6 kB [initial] [rendered]
chunk {polyfills} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 325 kB [initial] [rendered]
chunk {styles} styles.bundle.js, styles.bundle.js.map (styles) 63.7 kB [initial] [rendered]
chunk {vendor} vendor.bundle.js, vendor.bundle.js.map (vendor) 1.88 MB [initial] [rendered]
ファイル | 元の構成 | 新しい構成 |
---|---|---|
main.bundle.js | 61.4kB | 30.6kB |
vendor.bundle.js | 3.88MB | 1.88MB |
demo.module.chunk.js | 51.4kB | 2.04MB |
合計 | 3992.8kB | 3950.6kB |
特にvendor.bundleが2MBと大きく移動していることがわかる。今回のサンプルソースでは、ほぼAngularMaterialになる。
--prod
ビルドでの変化
元の構成でのビルド結果
Time: 28226ms
chunk {0} 0.aa68a6ffd1d68364462f.chunk.js () 8.04 kB [rendered]
chunk {1} main.212ed5e5d1c0616288fd.bundle.js (main) 563 kB [initial] [rendered]
chunk {2} polyfills.57c88da6a6d2a800f78f.bundle.js (polyfills) 126 kB [initial] [rendered]
chunk {3} styles.60f2c673d6a13f4d5d86.bundle.css (styles) 46.8 kB [initial] [rendered]
chunk {4} inline.735dc2ac6d85cc81d4a0.bundle.js (inline) 1.4 kB [entry] [rendered]
新しい構成でのビルド結果
Time: 11336ms
chunk {0} 0.aaac215c03d911a8600c.chunk.js () 213 kB [rendered]
chunk {1} polyfills.6a4c24b5ca42039de4fd.bundle.js (polyfills) 126 kB [initial] [rendered]
chunk {2} main.4a3d80967dfb1e57f705.bundle.js (main) 326 kB [initial] [rendered]
chunk {3} styles.60f2c673d6a13f4d5d86.bundle.css (styles) 46.8 kB [initial] [rendered]
chunk {4} inline.30a8d86ae20b87488c1f.bundle.js (inline) 1.4 kB [entry] [rendered]
ファイル | 元の構成 | 新しい構成 |
---|---|---|
main.bundle.js | 563kB | 326kB |
0.chunk.js | 8.04kB | 213kB |
合計 | 571.04kB | 539kB |
初回ロードで読み込まれるmain.bundleが約2/3に減った。
-
App Shell モデル。https://developers.google.com/web/fundamentals/architecture/app-shell?hl=ja ↩
-
Angularスタイルガイドを参考にしながら進めた。https://angular.io/guide/styleguide#application-structure-and-ngmodules ↩
-
https://github.com/studioTeaTwo/angular-starter/commit/5963e398d1492666c54ea9627849ebb3cc3b0214 ↩
-
https://github.com/studioTeaTwo/angular-starter/commit/6f90fdda2ebd0f04bf7dd2510922e393a9ab01dc ↩