Angular–AOT和JIT編譯

Escape Plan發表於2019-03-01

概覽

眾所周知, angular應用在可執行之前, angular應用中的元件和模板必須被轉化為可以被瀏覽器識別的javascript程式碼, 而這種轉化正是通過angualr自身的編譯器所執行的.

angular提供了兩種編譯方式, 即AOT(預編譯)和JIT(即使編譯), 其中JIT為預設的編譯方式

AOT即 Ahead of time, 是指在構建時進行編譯, 即在服務端即完成了編譯
JIt即 Just-in-Time, 在執行期間編譯該應用,也就是在應用載入時。

AOT vs JIT

實際上只有一個編譯器,兩者的區別只是編譯的時機和工具不同

JIT編譯導致執行期間的效能損耗。 由於需要在瀏覽器中執行這個編譯過程,檢視需要花更長時間才能渲染出來。 由於應用包含了Angular編譯器以及大量實際上並不需要的庫程式碼,所以檔案體積也會更大。 更大的應用需要更長的時間進行傳輸,載入也更慢。

編譯可以發現一些元件模板繫結錯誤。JIT編譯在執行時才揭露它們,那樣有點太晚了。

而預編譯(AOT)會在構建時編譯,這樣可以在早期截獲模板錯誤,提高應用效能。

#JiT編譯模式的流程

一個典型的jiT應用的開發流程大概是:

  • 使用TypeScript開發Angular應用
  • 使用tsc來編譯這個應用的ts程式碼
  • 打包
  • 壓縮
  • 部署

一旦把app部署好了,並且使用者在瀏覽器中開啟了這個app,下面這些事情會逐一進行:

  • 瀏覽器下載js程式碼
  • Angular啟動
  • Angular在瀏覽器中開始JiT編譯的過程,例如生成app中各個元件的js程式碼
  • 應用頁面得以渲染

相對的,使用AoT模式的應用的開發流程是:

  • 使用TypeScript開發Angular應用
  • 使用ngc來編譯應用
    • 使用Angular編譯器對模板進行編譯,生成TypeScript程式碼
    • TypesScript程式碼編譯為JavaScript程式碼
  • 打包
  • 壓縮
  • 部署

雖然前面的過程稍稍複雜,但是使用者這一側的事情就變簡單了:

  • 下載所有程式碼
  • Angular啟動
  • 應用頁面得以渲染

概括起來,Angular中的JitAoT的主要區別是:

  • 編譯過程發生的時機
  • JiT生成的是JS程式碼,而AoT生成的是TS程式碼。這主要是因為JiT是在瀏覽器中進行的,它完全沒必要生成TS程式碼,而是直接生產了JS程式碼。

深入AOT編譯

AOT編譯的配置

安裝npm依賴

npm install @angular/compiler-cli @angular/platform-server --save

複製程式碼

把下列npm便利指令碼新增到package.json中,以便用一條命令就可以完成編譯

"build:aot": "ngc -p tsconfig-aot.json
複製程式碼

@angular/compiler-cli包中提供的ngc編譯器來代替TypeScript編譯器(tsc)。

配置tsconfig-aot.json 檔案

只需將tsconfig.json 檔案複製過來進行修改即可

{
  "compilerOptions": {
    "target": "es5",
    "module": "es2015",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": ["es2015", "dom"],
    "noImplicitAny": true,
    "suppressImplicitAnyIndexErrors": true,
    "typeRoots": [
      "./node_modules/@types/"
    ]
  },

  "files": [
    "src/app/app.module.ts",
    "src/main.ts"
  ],

  "angularCompilerOptions": {
   "genDir": "aot",
   "skipMetadataEmit" : true
 }
}
複製程式碼

compilerOptions部分只修改了一個屬性:**把module設定為es2015(為後面搖樹優化做準備)

ngc區真正新增的內容是底部的angularCompilerOptions。 它的genDir屬性告訴編譯器把編譯結果儲存在新的aot目錄下

"skipMetadataEmit" : true屬性阻止編譯器為編譯後的應用生成後設資料檔案。 當輸出成TypeScript檔案時,後設資料並不是必須的,因此不需要包含它們。

啟動AOT編譯:

node_modules/.bin/ngc -p tsconfig-aot.json
複製程式碼

編譯完成後生成了一堆NgFactory檔案,不要編輯這些NgFactory!重新編譯時會替換這些檔案.

改變引導方式main.ts 檔案

platformBrowserDynamic.bootstrap改成使用platformBrowser().bootstrapModuleFactory並把AppModuleNgFactory的AOT編譯結果傳給它。

import { platformBrowser }    from `@angular/platform-browser`;
import { AppModuleNgFactory } from `../aot/src/app/app.module.ngfactory`;

console.log(`Running AOT compiled`);
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);

複製程式碼

搖樹優化(Tree shaking)

搖樹優化是指通過跟蹤importexport語句來對應用進行靜態分析,遍歷依賴圖譜,並且搖掉用不到的程式碼,通過移除原始碼和庫程式碼中用不到的部分,搖樹優化可以大幅縮減應用的下載體積

搖樹優化能夠在我們最終的Bundle中移除掉我們應用中沒有使用到的程式碼。這是減少應用程式佔用空間的最有效的技術之一。

目前webpack2已經支援tree shaking, 官網介紹的是Rollup在angular中的使用

知乎上關於tree shaking的介紹

開發器使用JIT, 產品期使用AOT

目前,AOT編譯和搖樹優化對開發來說,佔用的時間太多了。這將在未來得到改變。 當前的最佳實踐是在開發器使用JIT編譯,然後在釋出產品前切換到AOT編譯

相關文章