Angular 2 JIT vs AOT
轉載于:https://segmentfault.com/a/1190000008739157
在 Angular 應用程序中,包含了我們通過 Angular 提供的 API 實現的自定義指令。這些自定義指令對瀏覽器來說,都是無法識別的,因此每個 Angular 應用程序在運行前,都需要經歷一個編譯的階段。
在 Angular 2 中有兩種編譯模式:
-
JIT - Just-In-Time
-
AOT - Ahead-Of-Time
JIT - Just-In-Time
?
Just-in-Time 編譯模式開發流程
-
使用 TypeScript 開發 Angular 應用
-
運行 tsc 編譯 TypeScript 代碼
-
使用 Webpack 或 Gulp 等其他工具構建項目,如代碼壓縮、合并等
-
部署應用
應用部署后,當用戶通過瀏覽器訪問我們應用的時候,她將經歷以下步驟(非嚴格?CSP):
-
下載應用相關的資源,如 JavaScript 文件、圖片、樣式資源
-
Angular 啟動
-
Angular 進入 JiT 編譯模式,開始編譯我們應用中的指令或組件,生成相應的 JavaScript 代碼
-
應用完成渲染
AOT - Ahead-Of-Time
Ahead-Of-Time 編譯模式開發流程
-
使用 TypeScript 開發 Angular 應用
-
運行 ngc 編譯應用程序
-
使用 Angular Compiler 編譯模板,一般輸出 TypeScript 代碼
-
運行 tsc 編譯 TypeScript 代碼
-
-
使用 Webpack 或 Gulp 等其他工具構建項目,如代碼壓縮、合并等
-
部署應用
應用部署后,相比于 JIT 編譯模式,在 AOT 模式下用戶訪問我們的應用,只需經歷以下步驟:
-
下載應用相關的資源,如 JavaScript 文件、圖片、樣式資源
-
Angular 啟動
-
應用完成渲染
JIT vs AOT
Just-In-Time (JIT) compilation
Ahead-Of-Time (AOT) compilation
| 編譯平臺 | (Browser) 瀏覽器 | (Server) 服務器 |
| 編譯時機 | Runtime (運行時) | Build (構建階段) |
| 包大小 | 較大 | 較小 |
| 執行性能 | - | 更好 |
| 啟動時間 | - | 更短 |
除此之外 AOT 還有以下優點:
-
在客戶端我們不需要導入體積龐大的 angular 編譯器,這樣可以減少我們 JS 腳本庫的大小
-
使用 AOT 編譯后的應用,不再包含任何 HTML 片段,取而代之的是編譯生成的 TypeScript 代碼,這樣的話 TypeScript 編譯器就能提前發現錯誤。總而言之,采用 AOT 編譯模式,我們的模板是類型安全的。
另外感興趣的讀者,可以使用?source-map-explorer?工具查看不同模式下生成的?bundle?JS 文件中各種 JS 資源的占比。
AOT詳解
app.component.html
<button (click)="toggleHeading()">Toggle Heading</button> <h1 *ngIf="showHeading">Hello {{name}}</h1><h3>List of Heroes</h3> <div *ngFor="let hero of heroes">{{hero}}</div>app.component.ts
import { Component } from '@angular/core';@Component({moduleId: module.id,selector: 'my-app',templateUrl: './app.component.html' }) export class AppComponent {name: string = 'Angular';showHeading = true;heroes = ['Magneta', 'Bombasto', 'Magma', 'Tornado'];toggleHeading() {this.showHeading = !this.showHeading;} }安裝 npm 依賴:
npm?install @angular/compiler-cli @angular/platform-server --save在項目根目錄新增?tsconfig-aot.json?配置文件,內容如下:
{"compilerOptions": {"target": "es5","module": "es2015","moduleResolution": "node","sourceMap": true,"emitDecoratorMetadata": true,"experimentalDecorators": true,"lib": ["es2015", "dom"],"noImplicitAny": true,"suppressImplicitAnyIndexErrors": true},"files": ["src/app/app.module.ts","src/main.ts"],"angularCompilerOptions": {"genDir": "aot","skipMetadataEmit" : true} }執行 AoT 編譯:
-
node_modules/.bin/ngc -p tsconfig-aot.json
-
"node_modules/.bin/ngc" -p tsconfig-aot.json # Windows 用戶
命令成功運行后,在根目錄下會自動生成?aot?目錄,接下來我們來研究一下目錄中生成的文件:
*.component.ngfactory.ts
此類文件內包含以下定義:
-
View_{COMPONENT}_Host{COUNTER}?- 內部宿主組件
-
View_{COMPONENT}{COUNTER}?- 內部組件
上面的 {COMPONENT} 表示組件關聯的類名稱,{COUNTER} 是一個無符號整數。
它們都繼承于 AppView 并實現以下方法:
-
createInternal - 用于渲染組件
-
destroyInternal - 用于執行清理操作,如移除事件監聽、銷毀內嵌視圖
-
detectChangesInternal - 用于執行變化檢測
其中 detectChangesInternal 方法中包含了 JavaScript VM Friendly 的代碼,現在我們來看一下具體示例:
<h1?*ngIf="showHeading">Hello {{name}}</h1>該模板編譯后,detectChangesInternal 方法中的代碼如下:
detectChangesInternal(throwOnChange:boolean):void {// 計算h1標簽中文本元素的內容const currVal_2:any = import3.inlineInterpolate(1,'Hello ' ,this.parentView.context.name,'');// 判斷新值與舊值是否相等,若不相等則更新文本的內容,同時設置舊值為當前值if (import3.checkBinding(throwOnChange,this._expr_2,currVal_2)) {this.renderer.setText(this._text_1,currVal_2);this._expr_2 = currVal_2;}}接下來我們來看一下 Angular 1.x 中簡易版 $digest?:
// $scope.$watch('name', function(newValue, oldValue) {}) Scope.prototype.$watch = function (exp, fn) {'use strict';this.$$watchers.push({exp: exp,fn: fn,last: Utils.clone(this.$eval(exp))}); };Scope.prototype.$digest = function () {'use strict';var dirty, watcher, current, i;do {dirty = false; for (i = 0; i < this.$$watchers.length; i += 1) {watcher = this.$$watchers[i]; current = this.$eval(watcher.exp); // 計算新值if (!Utils.equals(watcher.last, current)) { // 比較新值和舊值watcher.last = Utils.clone(current); // 保存新值,用于下一次比較dirty = true;watcher.fn(current);}}} while (dirty); // 在Angular1.x的源碼中會有TTL值控制最大的檢測次數,避免出現死循環for (i = 0; i < this.$$children.length; i += 1) {this.$$children[i].$digest();} };從上面的代碼可以看出,Angular 1.x 中變化檢測涉及循環遍歷比 Angular 2 的變化檢測邏輯復雜很多。此外 Angular 2 的變化檢測是單向的,從根組件開始執行,具體如下圖:
更令人興奮的是,我們還可以靈活地設置 ChangeDetectionStrategy (變化檢測策略) 來進一步提供應用的性能。
AOT實戰
1.使用?ngc?命令行工具
示例項目:
-
ngc + Webpack
-
ngc + Rollup
-
ngc + library generation
2.使用?@ngtools/webpack?Webpack 2 插件
webpack.config.js 配置:
'use strict'; let path = require('path'); let AotPlugin = require('@ngtools/webpack').AotPlugin;module.exports = {module: {rules: [{ test: /\.ts/, use: '@ngtools/webpack' }]},plugins: [new AotPlugin({tsConfigPath: path.join(process.cwd(), 'tsconfig.json'),entryModule: path.join(process.cwd(), 'src/app/modules/main.module#MainModule')})] };示例項目:
-
Webpack 2 + plugin
-
Angular CLI
3.使用?@ultimate/aot-loader?Webpack 2 插件
webpack.config.js 配置:
'use strict'; let path = require('path'); let AotPlugin = require('@ultimate/aot-loader').AotPlugin;module.exports = {module: {rules: [{ test: /\.ts/, use: '@ultimate/aot-loader' }]},plugins: [new AotPlugin({tsConfig: path.join(process.cwd(), 'tsconfig.json'),entryModule: path.join(process.cwd(), 'src/app/modules/main.module#MainModule')})] };示例項目:
-
Webpack 2 + plugin
參考資源
-
angular cookbook aot-compiler
-
Ahead-of-Time Compilation in Angular
-
Multiple solutions for Angular Ahead of Time (AOT) Compilation
總結
以上是生活随笔為你收集整理的Angular 2 JIT vs AOT的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一点就到家
- 下一篇: 部落冲突一瓶工人药水可以减多少时间 《部