【问题标题】:What is the difference between Angular AOT and JIT compilerAngular AOT 和 JIT 编译器有什么区别
【发布时间】:2018-01-23 23:59:27
【问题描述】:

我正在深入研究 Angular 4,并试图理解编译。我读过 AOT 和 JIT 都将 TypeScript 编译为 JavaScript,无论是服务器端还是客户端。如果我在使用 Webpack 和 grunt 构建它并部署那个缩小的 javascript 时编译它,那么 AOT 和 JIT 是如何出现的?

【问题讨论】:

    标签: angular compilation


    【解决方案1】:

    在浏览器加载您的应用程序包后,Angular 编译器(打包在 vendor.bundle.js 中)执行来自 main.bundle.js 的模板的编译。这称为即时编译。该术语意味着编译发生在捆绑包到达浏览器的时间。

    JIT 编译的缺点是:

    1. 加载包和呈现 UI 之间存在时间间隔。这段时间花在了 JiT 编译上。在小型应用程序中这个时间很少,但在大型应用程序中,JiT 编译可能需要几秒钟,因此用户需要等待更长时间才能看到您的应用程序。

    2. Angular 编译器必须包含在 vendor.bundle.js 中,这会增加应用程序的大小。

    不鼓励在 prod 中使用 JiT 编译,我们希望在创建包之前将模板预编译为 JavaScript。这就是 Ahead-of-Time (AoT) 编译的意义所在。

    AoT 编译的优点是:

    1. 浏览器可以在您的应用程序加载后立即呈现 UI。无需等待代码编译。

    2. ngc 编译器未包含在 vendor.bundle.js 中,因此您的应用程序的大小可能会更小。

    如果您使用 Webpack,要执行 AoT,您需要调用 ngc 编译器。例如:

    "build:aot": "ngc -p tsconfig.json && webpack --config webpack.config.js"
    

    【讨论】:

    • 对,你还应该提到 AOT 的缺点——代码应该是静态可分析的,所以应该写成in a certain way
    【解决方案2】:

    我读过 AOT 和 JIT 都将 TypeScript 编译为 JavaScript 无论是服务器端还是客户端。

    不,这不是 AOT 和 JIT 编译器所做的。 TypeScript 使用 typescript 编译器转译成 JavaScript。

    角度编译器

    有两个编译器负责编译和代码生成的繁重工作:

    视图编译器编译组件模板和generates view factories。它解析模板内的表达式和 html 元素,并经过许多标准编译器阶段:

    parse-tree (lexer) -> abstract-syntax-tree (parser) -> intermediate-code-tree -> output
    

    提供者编译器编译模块提供者和generates module factories

    JIT 与 AOT

    这两个编译器用于 JIT 和 AOT 编译。 JIT 和 AOT 编译在获取与组件或模块关联的元数据的方式上有所不同:

    // the view compiler needs this data
    
    @Component({
       providers: ...
       template: ...
    })
    
    // the provider compiler needs this data
    
    @NgModule({
       providers: ...
    });
    

    JIT 编译器使用运行时来获取数据。装饰器函数 @Component@NgModule 被执行,they attach metadata 被 Angular 编译器使用反射能力(反射库)读取的组件或模块类。

    AOT 编译器使用 typescript 编译器提供的静态代码分析来提取元数据,并且不依赖于代码评估。因此,与 JIT 编译器相比,它有点受限,因为它无法评估非显式代码 - 例如它需要导出一个函数:

    // this module scoped function
    
    function declarations() {
      return [
        SomeComponent
      ]
    }
    
    // should be exported
    
    export function declarations() {
      return [
        SomeComponent
      ];
    }
    @NgModule({
      declarations: declarations(),
    })
    export class SomeModule {}
    

    同样,JIT 和 AOT 编译器大多是用于提取与组件或模块关联的元数据的包装器,它们都使用底层视图和提供程序编译器来生成工厂。

    如果我在使用 Webpack 和 grunt 构建它时编译它 部署那个缩小的 javascript AOT 和 JIT 是如何进入的 图片?

    Angular 提供了webpack plugin,它在构建期间从打字稿执行转译。该插件还可以对你的项目进行 AOT 编译,这样你就不会在 bundle 中包含 JIT 编译器,也不会在客户端执行编译。

    【讨论】:

      【解决方案3】:

      首先,Angular 正在远离 JIT 编译。我希望我们能在angular@5.x.x看到它

      Angular 编译器通过使用装饰器来获取您编写的所有元数据,例如

      @Component({
        selector: 'my-app',
        template: '<h1>Hello</h1>'m
        styles: [ ':host { display: block }' ]
      })
      
      constructor(
        @Host() @Optional() private parent: Parent,
        @Attribute('name') name: string) {} 
      
      @ViewChild('ref') ref;
      
      @ContentChildren(MyDir) children: QueryList<MyDir>;  
      
      @HostBinding('title') title;
      
      @HostListener('click') onClick() { ... }
      
      // and so on
      

      并对其进行分析。然后它获取模板和样式表并对其进行解析。编译器经历了许多我不会在这里描述的步骤。您可以查看描述编译过程的the following page。还有来自 Tobias Bosch 的 great talk。最后编译器创建 ngfactories 来实例化我们的应用程序。

      我认为 JIT 中 AOT 的主要区别是

      • Angular 运行编译的时间和地点
      • 编译器如何收集元数据
      • 编译器生成的ngfactory 的格式是什么

      JIT 编译器

      在每次页面加载时在浏览器的客户端运行

      它使用来自@angular/core 包的ReflectionCapabilities API 收集元数据。我们有以下选项可以在 JIT 模式下处理元数据:

      1) 直接 API

      例如我们可以像这样声明我们的组件

      export class AppComponent {
        static annotations = [
          new Component({
            selector: 'my-app',
            templateUrl: `./app.component.html`,
            styles: [ ':host { display: block }' ]
          })
        ];
      
        test: string;
      
        static propMetadata = {
            test: [new HostBinding('title')]
        };
      
      
        ngOnInit() {
          this.test = 'Some title'
        }
      }
      

      我们可以在 ES5 中编写类似的代码。 JIT 编译器将读取 annotationspropMetadata 静态属性。 AOT 编译器无法使用它。

      2) tsickle API

      export class AppComponent {
        static decorators = [{
            type: Component,
            args: [{
              selector: 'my-app',
              templateUrl: `./app.component.html`,
              styles: [ ':host { display: block }' ]
            },]
        }];
      
        test: string;
      
        static propDecorators = {
          'test': [{ type: HostBinding, args: ['title'] }]
        };
      
        ngOnInit() {
          this.test = 'Some title'
        }
      }
      

      上面的代码通常是由一些库生成的。 Angular 包也有相同的格式。这也不适用于aot。我们必须将 metadata.json 文件与我们的库一起提供以进行 AOT 编译。

      3) 通过调用装饰器获取元数据

      @Component({
        selector: 'my-app',
        templateUrl: `./app.component.html`
      })
      export class AppComponent {
        @HostBinding('title') test = 'Some title';
      }
      

      Typescript 编译器将前面的代码转换为

       var AppComponent = (function () {
          function AppComponent() {
              this.test = 'Some title';
          }
          return AppComponent;
      }());
      __decorate([
          HostBinding('title')
      ], AppComponent.prototype, "test", void 0);
      AppComponent = __decorate([
          Component({
              selector: 'my-app',
              templateUrl: "./app.component.html"
          })
      ], AppComponent);
      

      这段代码在 JIT 模式下执行,所以角度 calls Component decorator

      const TypeDecorator: TypeDecorator = <TypeDecorator>function TypeDecorator(cls: Type<any>) {
            // Use of Object.defineProperty is important since it creates non-enumerable property which
            // prevents the property is copied during subclassing.
            const annotations = cls.hasOwnProperty(ANNOTATIONS) ?
                (cls as any)[ANNOTATIONS] :
                Object.defineProperty(cls, ANNOTATIONS, {value: []})[ANNOTATIONS];
            annotations.push(annotationInstance);
            return cls;
      };
      

      今天是doesn't use Reflect api anymore。编译器直接从__annotations__属性读取数据

      if (typeOrFunc.hasOwnProperty(ANNOTATIONS)) {
        return (typeOrFunc as any)[ANNOTATIONS];
      }
      

      JIT 编译器生成 javascript ngfactories

      AOT 编译器

      在构建时使用 ngc 在服务器端 (nodejs) 上运行

      使用 AOT,没有运行时编译步骤。当我们在浏览器中运行我们的应用程序时,我们已经预编译了ngfactories。它一开始就为我们提供了更好的性能和延迟加载。我们也不再在我们的生产包中提供@angular/compiler 代码。但是由于我们的ngfactories 代码,我们的捆绑包可以显着增长。

      AOT 编译器使用typescript api 来分析打字稿代码。要获取元数据编译器,需要通过 StaticSymbolResolverMetadataCollector API。

      所以它需要app.component.ts 文件并创建打字稿对象模型。所以我们的AppComponent 类将呈现为NodeObject 类型为229 (ClassDeclaration)

      我们可以看到这个对象有decorators 属性

      以及由 Angular 团队编写并调用 tsc-wrapper does hard work 来提取此元数据的特殊 typescript 包装器。

      当编译器meets d.ts file 尝试从metadata.json 获取元数据时:

      if (DTS.test(filePath)) {
        var metadataPath = filePath.replace(DTS, '.metadata.json');
        if (this.context.fileExists(metadataPath)) {
          return this.readMetadata(metadataPath, filePath);
        }
        else {
          // If there is a .d.ts file but no metadata file we need to produce a
          // v3 metadata from the .d.ts file as v3 includes the exports we need
          // to resolve symbols.
          return [this.upgradeVersion1Metadata({ '__symbolic': 'module', 'version': 1, 'metadata': {} }, filePath)];
        }
      }
      

      最后 AOT 编译器使用TypeScriptEmitter生成打字稿 ngfactories(角度

      另见

      【讨论】:

      • 虽然我认为 Max K Wizard 是一个传奇,但这应该是公认的答案。非常全面。
      猜你喜欢
      • 2017-03-13
      • 1970-01-01
      • 2018-09-23
      • 2019-03-01
      • 2023-04-02
      • 2019-11-10
      • 2017-05-17
      • 2019-04-20
      • 1970-01-01
      相关资源
      最近更新 更多