Angular V13
リリース後、歴史的な領域から外れて、Ivy
コンパイルモードがデフォルトになります。View Engine
を使用するView Engine
と、ビルドngSummary.js
前のビルドngFactory.js
テンプレートのコンパイル方法は存在しなくなります。Ivy
モードでtemplate
は、命令演算関数はDOM抽象構造に変換され@Component
、デコレータ情報などは静的プロパティに変換されます。
この記事は、Angular
コンパイルツールngc
のワークフローの予備調査と要約です。
ビューエンジンのレビュー
元のコンパイルモードでは、最初にグローバルメタデータ情報が初期化中に収集され、次にテンプレートとディレクティブの定義が生成されます。
最初に、セレクター、依存関係、@ Input、および@Output情報を含むメタデータ情報を収集します。
次に、ファクトリ関数を試して、テンプレートのコンパイル結果を返します。
この動作モードにより、ライブラリのメタデータ情報の依存関係はビルド時にのみ決定できます。そのため、ライブラリはコンパイル済み製品を公開できずmetadata
、データを公開して、アプリケーションのビルド時にコンパイルすることしかできません。
さらに、証明書利用者のメタデータ情報を再生成する必要があり、元の情報factory
もになりますので、証明書利用者の変更は、証明書利用者の再コンパイルにもつながりinvalid
ます。
アイビーに会う
Ivy
:mental modal
デコレータはコンパイラです。class transfomer
これは、定義を取得するための入力パラメーターとして理解できます。。
通常情况,装饰器的上述转换过程仅需要自身的信息就足够了,除了一个例外:@Component
。因为它具有template
,而template
可能会依赖selectors
,directives
and pipes,
因此需要获取NgModule
中Declaration
和Import
的信息。
初始化时会进行全局分析,建立import graph
和 Semantic dependency graph
,确定依赖关系,解决了之前版本的历史问题:修改被依赖方的代码,不再会让依赖方也需要重新编译。
模板内容不再由factory
生成,而是编译成直接的DOM抽象结构指令函数操作:没有用到的DOM指令函数可以被tree-shaking
。
ngc
Angular
编译器ngc
的主要任务有三个:
- 编译
decorators
,包括component
和template
- 对
template
应用类型检查 - 变化发生时进行高效率的
re-compile
为了完成上述3个目标,ngc
的工作流程由下面几步完成:
Step 1. 创建TypeScript
编译器实例,并扩展Angular
特性
在TypeScript
编译器中,待编译的程序被表示为ts.Program
实例,包含了待编译的文件集合,依赖项的类型信息,和编译过程定义的参数。
通过入口文件遍历,得到所有的文件和依赖列表。不需要编译的依赖(如引用的库)会读取其.d.ts
文件得到类型信息。
对于自己写的文件(my.component.ts
),ngc
会添加一个额外的.ngtypecheck
后缀的文件(my.component.ngtypecheck.ts
)到ts.Program
来做内部的类型检查工作用。
对于特定的参数可能会有额外的编译结果,比如生成.ngfactory
文件来做View Engine
的向后扩展。
Step 2. 解析装饰器信息(Individual analysis)
将装饰器代码转换为对应的definition
信息,包括template
,selector
,view encapsulation
等。取决于decorator type
。
这就要求编译器具备读取表达式的能力(在不运行代码的情况下),比如:
const MY_SELECTOR = 'my-cmp';
@Component(
{
selector: MY_SELECTOR,
template: '...',
}
)
export class MyCmp {}
复制代码
编译器需要去解析MY_SELECTOR
得到'my-cmp'。这也让开发者能更灵活的定义装饰器信息。
Step 3. 收集依赖信息,建立依赖关系(Global analysis)
完成了上一步的单独分析后,编译器需要确定各个decorator
信息之间的依赖关系。组件在依赖其他组件时不需要直接导入,而是在template
中使用类css class
的selector
即可,这个建立依赖的工作是NgModule
完成的。
因此,NgModule
需要确定两个scope
:
Compilation Scope
:所有声明的和导入其他NgModule中声明的依赖Export Scope
:导出的依赖
之后,ngc
会建立两个graph
:
- import graph:所有依赖项的信息。
- Semantic dependency graph:依赖项之间的语意关联信息。
对于库等不需要编译的内容,会通过其所提供的.d.ts
文件来获取类型信息作为dependencies。
Step 4. 模版类型检查
ngc
将Template
转换成代表相同操作的类型层面的TypeScript Code
(类型计算/类型体操),然后交给TypeScript
判断是否有错误。
比如:
<span *ngFor="let user of users">{{user.name}}</span>
复制代码
会被转换成
import * as i0 from './test';
import * as i1 from '@angular/common';
import * as i2 from '@angular/core';
const _ctor1: <T = any, U extends i2.NgIterable<T> = any>(init: Pick<i1.NgForOf<T, U>, "ngForOf" | "ngForTrackBy" | "ngForTemplate">) => i1.NgForOf<T, U> = null!;
/*tcb1*/
function _tcb1(ctx: i0.TestCmp) {
if (true) {
var _t1 /*T:DIR*/ /*165,197*/ = _ctor1({
"ngForOf": (((ctx).users /*190,195*/) /*190,195*/) /*187,195*/,
"ngForTrackBy": null as any,
"ngForTemplate": null as any
})
/*D:ignore*/;
_t1.ngForOf /*187,189*/ = (((ctx).users /*190,195*/) /*190,195*/) /*187,195*/;
var _t2: any = null!;
if (i1.NgForOf.ngTemplateContextGuard(_t1, _t2) /*165,216*/) {
var _t3 /*182,186*/ = _t2.$implicit /*178,187*/; "" + (((_t3 /*199,203*/).name /*204,208*/) /*199,208*/);
}
}
}
复制代码
(属实是看不懂)
Step 5. 完成
この時点でngc
、プログラムは解析され、fatal errors
それ以上存在しないことが確認され、コードTypescript
はコンパイラーによって生成されJavaScript
ます。このプロセス中decorator
に削除され、いくつかの静的プロパティがクラス定義に追加されます(例:selector
、、style
などtemplate
)componentDefinition
。
export class MyComponent {
name: string;
static ɵcmp = core.ɵɵdefineComponent({
type: HelloComponent,
tag: 'hello-component',
factory: () => new HelloComponent(),
template: function (rf, ctx) {
if (rf & RenderFlags.Create) {
core.ɵɵelementStart(0, 'div'); core.ɵɵtext(1);
core.ɵɵelementEnd();
}
}
});
}
复制代码
ステップ6.更新
インポートグラフに従って、変更された部分を再コンパイルします。
セマンティック依存関係グラフに従って前のステップの変更結果を分析し、他の部分に影響を与えるかどうかを判断します。
2つのツールを組み合わせて使用すると、更新のAngular
実行方法に役立ちます。Incremental
Incremental Dom
とVirtual Dom
updateメソッドの違いについて:
Virtual Dom
レンダリング中に、古いものと比較するために新しいものがRender関数によって生成され、変更の結果がアルゴリズムによって取得されます。Virtual Dom
diff
Incremental Dom
変更した場所で命令演算機能を再生成することです。したがって、メモリ消費量が少なく、未使用の命令機能を最適化することができます。
参照: