之前用 Angular 4 (Angular 2) 開發後台系統,使用者都是在 PC 上面使用,網站啟動速度都很快,所以沒有使用到 Ahead-of-Time (AOT) 預先編譯的需求。
最近新產品上線,遇到很多 Android 的使用者開啟網站超級慢,舊一點的機型甚至開一分鐘才有畫面。事後才趕緊補上 AOT 編譯。
本篇將介紹用 Webpack 預先編譯 AOT 編譯。
AOT vs JIT
開發 Angular 所撰寫的 TypeScript 透過 TypeScript Compiler 產生出來的 *.js
檔,是屬於 Angular 的 Source Code,必須經由 Angular Compiler,才能在瀏覽器中執行。
而 Angular Compiler 可以在兩個不同的階段進行編譯:
Just-in-Time (JIT)
Angular 預設是使用 Just-in-Time (JIT) 即時編譯,等瀏覽器下載完 *.js
檔案後,會在用戶端的瀏覽器編譯 Angular 的 JS 程式碼,接著才會渲染畫面。
優點:
*.js
檔案是 Angular 的原始碼,所以檔案較小。
缺點:
在用戶端的瀏覽器編譯,所以網站啟動速度會較慢。
Ahead-of-Time (AOT)
Ahead-of-Time (AOT) 預先編譯,是在程式發佈之前就透過 Angular Compiler 進行編譯,所以瀏覽器下載完的 *.js
檔案,就可以直接被執行,然後渲染畫面。
優點:
減少掉了在用戶端的瀏覽器編譯的時間,所以網站啟動速度會較快。
缺點:
由於
*.js
是經過編譯後的檔案,所以檔案會較大。
範例程式碼
我用之前Angular 4 教學 - 從頭開始的範例延伸。
初始結構檔案結構如下:
1 | index.html # 起始頁面 |
安裝 Webpack 套件
安裝 Webpack 及接下來範例打包所需的開發套件,指令如下:
1 | npm install --save-dev webpack angular2-template-loader awesome-typescript-loader raw-loader |
Webpack 設定
新增 webpack.config.js
1 | var webpack = require("webpack"); |
執行 webpack
打包指令如下:
1 | webpack -p |
修改 index.html
參考 *.js
的位置:
1 |
|
打開網頁用瀏覽器的開發工具,看到 bundle-vendors.js
及 bundle.js
檔案大小及載入時間:
到目前為止,只是改成用 Webpack 打包 Angular 的 Source Code,所以還只是 JIT 編譯。
由於本篇範例實在是太簡單了,看到載入的速度很快,才 400/ms 左右。
安裝 AOT 套件
AOT 編譯所需的套件,除了 bundle-vendors
設定中的 11 個套件,還需要額外安裝 @angular/compiler-cli
及 @angular/platform-server
,指令如下:
1 | npm install --save @angular/compiler-cli @angular/platform-server |
TypeScript 設定
編輯 tsconfig.json
,設定 AOT 編譯後的檔案輸出位置:
1 | { |
基本上 tsconfig.json
的設定跟原本差不多,新增了 files
及 angularCompilerOptions
。
files
指定 Module 的位置,如果是 Lazy Loading,這邊就要加入所有 Lazy Loading 的 Module。genDir
指定 AOT 編譯後的檔案輸出位置。
我的範例程式中,為了保留 AOT 及 JIT 的差異,所以另外新增了一個
tsconfig-aot.json
的檔案,並保留原本的tsconfig.json
。
AOT 編譯
套件安裝完也設定完成後,就可以用 ngc
指令進行 AOT 編譯:
1 | node_modules/.bin/ngc -p tsconfig-aot.json |
編譯成功後,檔案結構如下:
1 | index.html # 起始頁面 |
更新程式進入點
編輯 main.ts
1 | // JIT 啟動 AppModule 用法 |
由於 AppModule
已經透過 AOT 編譯成 AppModuleNgFactory
,所以載入 Module 不再使用 platformBrowserDynamic
動態載入,改為用 platformBrowser
載入 AppModuleNgFactory
。
再次執行 Webpack 打包後,打開網頁用瀏覽器的開發工具,看到 bundle-vendors.js
及 bundle.js
檔案大小及載入時間:
可以看到 bundle.js
的檔案大小差異非常的大,從原本的 1.8 KB 變成 4.6 KB。
頁面載入速度稍微快了一點,從原本的 400/ms 變為 250/ms 左右,因為範例太簡單,所以速度差的沒有很多。
但隨著系統規模越做越大及程式複雜度提升,速度就會有非常明顯的差異。
bundle-vendors.js
變小是因為我把@angular/*
移除了。
補充
換成用 Webpack 打包後,npm 套件 systemjs
就用不到了,可以從 package.json
中移除,或用指令移除:
1 | npm uninstall systemjs --save |
system.config.js 也可以隨之移除。
改成用 AOT 編譯後,前端不再需要動態編譯,所以在 bundle-vendors
中可以把 @angular/*
移除:
1 | var webpack = require("webpack"); |
建議
根據我的經驗以及網路上的資訊,PC 及 iPhone 在執行 Angular JIT 編譯時,速度都還算蠻快的,而且差異不太大。
而 Android 不論是新舊機型或任何瀏覽器,執行 Angular JIT 編譯的速度都明顯比 PC 及 iPhone 慢,舊一點的機型甚至開一分鐘才有畫面。
類似的問題:angular2 loading slow on android(had use lazy loading)
所以,如果產品需要支援手機或嵌入式裝置等,建議一開始就使用 AOT。
如果只需要支援 PC 版,用 JIT 會較省下載速度及流量,可以比較不用擔心啟動效率問題。
程式碼下載
參考
Ahead-of-Time Compilation
Angular2 AOT with Webpack and Lazy Loading
angular2 loading slow on android(had use lazy loading)