同一项目上的角度 AOT 和 JIT

Posted

技术标签:

【中文标题】同一项目上的角度 AOT 和 JIT【英文标题】:angular AOT and JIT on same project 【发布时间】:2018-08-21 08:09:55 【问题描述】:

在 angular5 上,我尝试对我的大部分模块/组件进行同一个项目的 AOT 编译...但我有一部分需要 JIT 编译。

对于第二部分,html 来自 Ajax 请求并包含一些必须由 angular 编译的组件标记。为了管理这部分,我使用如下指令:

export class ArticleLiveDirective implements OnInit, OnChanges, OnDestroy 

    // [...]    

    constructor(
        private container: ViewContainerRef,
        private compiler: Compiler
    )  

    // [...]

    private addHtmlComponent(template: string, properties: any = ) 
        this.container.clear();
        //Force always rootDomElement.
        const divTag = document.createElement('div');
        divTag.setAttribute('id',this.currentId);
        divTag.innerHTML = template;
        template = divTag.outerHTML;

        // We create dynamic component with injected template
        @Component( template )
        class ArticleLIveComponent implements OnInit, OnChanges, OnDestroy 
            constructor(
                private articleService: ArticleService
            ) 
            ngOnInit() 
            ngOnChanges(changes: SimpleChanges) 
            ngOnDestroy() 
            goToPage($event: Event, pagination: string) 
                this.articleService.askToChangeArticle(pagination);
                //Stop propagation
                $event.stopPropagation();
                return false;
            

        

        // we declare module with all dependencies
        @NgModule(
            declarations: [
                ArticleLIveComponent
            ],
            imports: [
                BrowserModule,
                MatTabsModule
            ],
            providers: []
        )
        class ArticleLiveModule 

        // we compile it
        const mod = this.compiler.compileModuleAndAllComponentsSync(ArticleLiveModule);
        const factory = mod.componentFactories.find((comp) =>
            comp.componentType === ArticleLIveComponent
        );
        // fetch instance of fresh crafted component
        const component = this.container.createComponent(factory);
        // we inject parameter.
        Object.assign(component.instance, properties);
    

如您所见,我可以调用 addHtmlComponent 方法在运行时使用自定义 HTML 作为模板编译新组件。

我的模板看起来像:

<div>
<h2>Foo bar</h2>
<mat-tab-group>
  <mat-tab label="Tab 1">Content 1</mat-tab>
  <mat-tab label="Tab 2">Content 2</mat-tab>
</mat-tab-group>
<p>Other content</p>

一切正常,直到我切换到 AOT 编译(仅供参考:https://github.com/angular/angular-cli/tree/master/packages/%40ngtools/webpack)

可能的原因: 我猜的主要原因是因为 AOT 编译从输出编译包中删除了 Angular 的“编译器”部分。 我的尝试 - 我试图直接在我的代码上要求它,但仍然不存在。 - 我试图检查像角度(或角度材料)这样的网站如何处理它。但不适合我的情况。事实上,两者都已经编译了 AOT 版本中的所有示例。动态部分是“只是”样本周围的内容。

如果你想检查角度材料是如何做到的: 每个组件的所有网站示例:https://github.com/angular/material2/tree/master/src/material-examples

然后他们有 loader : https://github.com/angular/material.angular.io/blob/master/src/app/shared/doc-viewer/doc-viewer.ts#L85

这可能是正确的做法,但我不知道如何调整它来管理动态的 Tab 内容。


编辑:我在这里添加了示例:https://github.com/yanis-git/aot-jit-angular(分支大师)

正如你将看到的,AOT 编译从包中删除了墙编译器,这个结果:

Module not found: Error: Can't resolve '@angular/compiler/src/config'

我尝试在 AppModule 上强制编译器工厂,但仍然没有结果。

我在同一个 repo 上有另一个示例,但是在分支“lazy-jit”上,现在我在输出的包中嵌入了编译器,但新的错误出现在我身上:

ERROR Error: No NgModule metadata found for 'ArticleLiveModule'.

谁看起来和这个问题完全一样:https://github.com/angular/angular/issues/16033

【问题讨论】:

Angular 材质不会在运行时编译模板。他们使用 entryComponents github.com/angular/material2/blob/… 是的,这是我试图解释的。我不能使用相同的材​​料网站方式,因为它们使用预编译的组件示例。 github.com/angular/angular/issues/20639#issuecomment-347149868 感谢 yurzui 这个资源,我已经尝试过了,但不幸的是。没有 Angular 的编译器部分的结果编译。 你能创建简单的应用程序来重现它吗? 【参考方案1】:

试试这个:

    import  Compiler, COMPILER_OPTIONS, CompilerFactory, NgModule  from '@angular/core';
    import  BrowserModule,  from '@angular/platform-browser';
    import  FormsModule  from '@angular/forms';

    import  AppComponent  from './app.component';
    import  HelloComponent  from './hello.component';


    import  JitCompilerFactory  from '@angular/platform-browser-dynamic';

    export function createCompiler(compilerFactory: CompilerFactory) 
      return compilerFactory.createCompiler();
    


    @NgModule(
      providers: [
         provide: COMPILER_OPTIONS, useValue: , multi: true ,
         provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS] ,
         provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory] 
      ],
      imports: [BrowserModule, FormsModule],
      declarations: [AppComponent, HelloComponent],
      bootstrap: [AppComponent]
    )
    export class AppModule  

CODE EXAMPLE

但是 JitCompiler 仍然无法创建依赖注入树。一世 怀疑 @Injectable 将从 AOT 部分中删除。但我做不到你的 把戏。

在上面的代码示例中,NgModule 和组件没有装饰器。因此,这意味着也没有 @Injectable 并且他们无法注入 providers。那么为什么我们不写 @NgModule@Component @Injectable 装饰器,而只写到 Services ? 因为,他们有一个装饰器(@NgModule/@Components),Services 没有。它们的装饰器足以让 Angular 知道它们是可注入的。

CODE EXAMPLE 带 DI。

更新: 创建自定义包装器 CustomNgModuleCustomComponentCustomInjectable 装饰器:

export function CustomComponent(annotation: any) 
    return function (target: Function) 
        const component = new Component(annotation);
        Component(component)(target);

    ;


export function CustomNgModule(annotation: any) 
    return function (target: Function) 
        const ngModule = new NgModule(annotation);
        NgModule(ngModule)(target);
    ;



export function CustomInjectable() 
  return function (target: Function) 
      const injectable = new Injectable();
      Injectable()(target);
  ;

使用AOT 标志构建时,Angular-CLI 看起来像清理捆绑包 来自需要编译的部分代码的本机装饰器 动态的。

在你想要的地方dynamically用组件编译模块 在 AOTDI 功能中,替换原生装饰器 (NgModule/Injectable...) 自定义一个来保存装饰器 AOT编译模式:

lazy.module.ts:

@CustomComponent(
  selector: 'lazy-component',
  template: 'Lazy-loaded component. name:  name.Service 
           service.foo()!',
  //providers: [SampleService]
)
export class LazyComponent 
  name;
  constructor(public service: SampleService) 
    console.log(service);
    console.log(service.foo());
  


@CustomNgModule(
  declarations: [LazyComponent],
  providers: [SampleService]
)
export class LazyModule 

app.component.ts:

...
 ngAfterViewInit() 

    this.compiler.compileModuleAndAllComponentsAsync(LazyModule)
      .then((factories) => 
        const f = factories.componentFactories[0];    
        const cmpRef = this.vc.createComponent(f);    
        cmpRef.instance.name = 'dynamic';
      );
  
...

CODE EXAMPLE 3

【讨论】:

你好 Yerkon,我已经在我附加的代码示例上尝试过:github.com/yanis-git/aot-jit-angular/blob/master/src/app/… ------ 这个结果:错误错误:没有为“ArticleLiveModule”找到 NgModule 元数据。您对工作进行抽样是因为 stackblitz 不能在 AoT 中编译 @Yanis-git,下载源代码(导出按钮)。我在本地对其进行了测试:ng build --prod. 是真的,您的解决方案导入正确的 JitCompiler 并从组件/模块中保留元数据。但是 JitCompiler 仍然无法创建依赖注入树。我怀疑 @Injectable 将从 AOT 部分中删除。但我不能做你的伎俩。原始示例:stackblitz.com/edit/… 请查看控制台日志以获取更多详细信息。 感谢您的帮助,所以如果我理解得很好,不可能在这个组件上同时拥有“动态模板定义”和模块/服务注入? @Yanis-git,不,这是可能的。稍后我将发布代码示例,其中使用 aot 标志动态创建带有 dI 的组件/模块【参考方案2】:

我最近遇到了同样的问题,暂时放弃了解决。

因为angular removes metadata on production build,这不能(暂时)完成。

我们应该在问题 8896 关闭后重试。

【讨论】:

是的,我注意到了同样的事情,不是删除的角度,而是 ng-cli。我也尝试过这个技巧:gist.github.com/p3x-robot/e12ed76acb7033638b4179149546bb73,输出不被重视。 JitCompiler 将在 AoT 中工作,所有代码看起来都可以工作。但是 Angular 在控制台日志上抛出了很多关于错过元数据的错误。 不幸的是,最好不要走老路,等待官方解决方案

以上是关于同一项目上的角度 AOT 和 JIT的主要内容,如果未能解决你的问题,请参考以下文章

使用 AOT 的类型提供程序中的角度条件

JVM 虚拟机 AOT 和 JIT 即时编译

JVM 虚拟机 AOT 和 JIT 即时编译

理解Java的JIT和AOT

JIT和AOT编译详解

ART中AOT和JIT编译器的区别