Angularコンパイラのワークフローを最初に見る

Angular V13リリース、歴史的な領域から外れて、Ivyコンパイルモードがデフォルトになります。View Engine

を使用するView Engineと、ビルドngSummary.js前のビルドngFactory.jsテンプレートのコンパイル方法は存在しなくなります。Ivyモードでtemplateは、命令演算関数はDOM抽象構造に変換され@Component、デコレータ情報などは静的プロパティに変換されます。

この記事は、Angularコンパイルツールngcのワークフローの予備調査と要約です。

ビューエンジンのレビュー

元のコンパイルモードでは、最初にグローバルメタデータ情報が初期化中に収集され、次にテンプレートとディレクティブの定義が生成されます。

Snipaste_2022-04-08_14-36-16.png

最初に、セレクター依存関係、@ Input、および@Output情報を含むメタデータ情報を収集します。

image.png

次に、ファクトリ関数を試して、テンプレートのコンパイル結果を返します。

image.png

この動作モードにより、ライブラリのメタデータ情報の依存関係はビルド時にのみ決定できます。そのため、ライブラリはコンパイル済み製品を公開できずmetadata、データを公開して、アプリケーションのビルド時にコンパイルすることしかできません。

さらに、証明書利用者のメタデータ情報を再生成する必要があり、元の情報factoryもになりますので、証明書利用者の変更は、証明書利用者の再コンパイルにもつながりinvalidます。

アイビーに会う

Ivymental modalデコレータはコンパイラです。class transfomerこれは、定義を取得するための入力パラメーターとして理解できます。

通常情况,装饰器的上述转换过程仅需要自身的信息就足够了,除了一个例外:@Component。因为它具有template,而template可能会依赖selectorsdirectives and pipes,因此需要获取NgModuleDeclarationImport的信息。

初始化时会进行全局分析,建立import graphSemantic dependency graph ,确定依赖关系,解决了之前版本的历史问题:修改被依赖方的代码,不再会让依赖方也需要重新编译。

模板内容不再由factory生成,而是编译成直接的DOM抽象结构指令函数操作:没有用到的DOM指令函数可以被tree-shakingimage.png

ngc

Angular编译器ngc的主要任务有三个:

  1. 编译decorators,包括componenttemplate
  2. template应用类型检查
  3. 变化发生时进行高效率的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信息,包括templateselectorview 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 classselector即可,这个建立依赖的工作是NgModule完成的。

因此,NgModule需要确定两个scope

  1. Compilation Scope:所有声明的和导入其他NgModule中声明的依赖
  2. Export Scope:导出的依赖

之后,ngc会建立两个graph

  1. import graph:所有依赖项的信息。
  2. Semantic dependency graph:依赖项之间的语意关联信息。

对于库等不需要编译的内容,会通过其所提供的.d.ts文件来获取类型信息作为dependencies。

Step 4. 模版类型检查

ngcTemplate转换成代表相同操作的类型层面的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などtemplatecomponentDefinition

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 DomVirtual Domupdateメソッドの違いについて:

Virtual Domレンダリング中に、古いものと比較するために新しいものがRender関数によって生成され、変更の結果がアルゴリズムによって取得されます。Virtual Domdiff

Incremental Dom変更した場所で命令演算機能を再生成することですしたがって、メモリ消費量が少なく、未使用の命令機能を最適化することができます。

参照:

  1. Angularコンパイラのしくみ
  2. Angular Ivyを理解する:インクリメンタルDOMと仮想DOM

おすすめ

転載: juejin.im/post/7084355816949547021