Angular 6/7 AOT:动态模板渲染 - 为模块加载 JitCompiler

Posted

技术标签:

【中文标题】Angular 6/7 AOT:动态模板渲染 - 为模块加载 JitCompiler【英文标题】:Angular 6/7 AOT: Dynamic template render - load JitCompiler for module 【发布时间】:2019-05-21 00:29:02 【问题描述】:

我在从 API 响应“即时”构建模板时遇到问题,但仅限于 AoT 构建。

我从后端收到这样的回复:

<h1>Title...</h1> 
<some-component></some-componen> 
<p>other content</p>

我想像普通的 Angular 模板一样解析它。

我的组件的简化代码如下所示:


        import 
          Compiler,
          Component,
          ComponentFactory,
          ComponentRef,
          Injector,
          Input,
          NgModule,
          OnChanges,
          OnDestroy,
          OnInit,
          ViewContainerRef
         from '@angular/core';
        import  CommonModule  from '@angular/common';
        import  RouterModule  from '@angular/router';

        export async function createComponentFactory(compiler: Compiler, metadata: Component): Promise> 
          const cmpClass = class DynamicComponent 
          ;
          const decoratedCmp = Component(metadata)(cmpClass);

          // IMPORT ALL MODULES HERE!!!
          @NgModule(imports: [CommonModule, RouterModule], declarations: [decoratedCmp])
          class DynamichtmlModule 
          

          const moduleWithComponentFactory = await compiler.compileModuleAndAllComponentsAsync(DynamicHtmlModule);
          return moduleWithComponentFactory.componentFactories.find(x => x.componentType === decoratedCmp);
        

        @Component(
          selector: 'html-renderer',
          templateUrl: './html-renderer.component.html',
          styleUrls: ['./html-renderer.component.scss']
        )
        export class HtmlRendererComponent implements OnInit, OnChanges, OnDestroy 

          @Input() content: string; 
          cmpRef: ComponentRef;

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

          ngOnInit(): void 
            console.log('init...')
            console.log(this.compiler)
          

          ngOnDestroy() 
            if (this.cmpRef) 
              this.cmpRef.destroy();
            
          

          ngOnChanges() 
            const html = this.content;
            if (!html)  return; 

            if (this.cmpRef) 
              this.cmpRef.destroy();
            

            const compMetadata = new Component(
              selector: 'dynamic-selector',
              template: this.content,
            );

            createComponentFactory(this.compiler, compMetadata)
              .then(factory => 
                const injector = Injector.create(providers: [], parent: this.vcRef.injector);
                this.cmpRef = this.vcRef.createComponent(factory, 0, injector, []);
              );
          


        

所以,我将整个数据传入content 输入,然后通过compileModuleAndAllComponentsAsync 方法(https://angular.io/api/core/Compiler#compilemoduleandallcomponentssync)编译所有组件 并且所有工作都在 JIT 构建中运行

我想在 AoT 编译中完成这项工作,因为现在我收到一个错误: Runtime Compiler is not loaded 在示例代码上使用 AoT 构建时

我也尝试在providers[]app.module.ts 中提供编译器,但它也不起作用:

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

    provide: COMPILER_OPTIONS, useValue: , multi: true,
    provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS],
    provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory],

我的问题:有没有办法将延迟加载的模块包含在 JIT 编译器中以访问其方法?

我找到了一些相关的问题,但没有答案:

Error while using @angular compiler in Angular 5 and AOT-Build

编辑 15.01.2019 这是 stackblitz.com 上的一个有效 JIT 示例,其中包含插值和数据绑定测试: https://stackblitz.com/github/lyczos/angular-dynamic-html-renderer

编辑 05.01.2020 目前,我已经开始使用builder.io Steve(该项目的作者)正在使用 Web 组件来使其工作。

【问题讨论】:

其实我觉得这是个好问题。对我来说看起来不错! :) 让我改述一下:您想在不加载编译器的情况下使用编译器。很抱歉,这里有一个逻辑问题……您要么使用 JIT(带编译器)或 AOT(不带编译器)。 @Hatef 这怎么可能是个好问题? @smnbbrv 对,但是有什么方法可以将编译器(即时)附加到延迟加载的模块上,以免增加初始应用程序加载时间?例如我在这里看到了这个,但它不适用于 Angular 6/7 github.com/alexzuza/angular2-build-examples/tree/master/… @smnbbrv 好的,知道了。您是否看到任何其他可能的解决方案/想法来“即时”构建模板? @smnbbrv 它的格式很好,包含了经过尝试的代码,添加了相关问题的链接,绝对是关于主题的,他们的问题很明确 - 如果 OP 的逻辑/他们试图做的事情没有对您/我们来说没有意义,这并不是一个坏问题。 【参考方案1】:

首先,我很抱歉写这个作为答案,但它太长了,不能作为评论。

可以按照您的要求进行操作。事实上,我实际上在今年的 ng-conf 上提出了这个确切的问题。在 Max Koretskyi(又名 angularindepth.com 的“ng-wizard”作者)就这个主题进行了一次会议后,我与他进行了交谈。

我必须警告你,尽管他提供的解决方案非常复杂、笨​​拙,而且你不能指望它不会在未来的 Angular 版本中破坏,因为你试图完成的事情违背了 Angular 框架,而这正是Angular 团队正试图阻止人们这样做。真的,试图维护它只是一场噩梦,任何新的开发者都可能会弹出他们的顶峰,试图理解我所做的事情。哎呀,如果我一年多后回顾它,我什至可能都不知道我做了什么。

最终,我决定放弃 AOT 并使用 JIT 部署我的应用程序,从那以后我没有后悔我的决定。如果你决定你真的想进一步追求这个,我建议你联系 Max。从我在 ng-conf 收集到的信息来看,他是一个非常友好的人,如果他们有问题,他会公开邀请人们与他联系。希望对您有所帮助,祝您好运! :)

【讨论】:

我有工作解决方案 :) 看看@Narm : builder.io【参考方案2】:

我去年遇到了同样的问题,并且能够找到解决方法。我在样式指南中使用了动态生成的角度组件。这是一个适用于 AOT 编译的工作示例:

https://github.com/johncrim/angular-dynamic-styleguide

import 'core-js/es7/reflect'; 添加到polyfills.ts 是关键的非显而易见的技巧。

使用ng build --prod 运行动态编译的组件也需要

 "buildOptimizer": false,

在 angular.json 中的生产配置中。请注意,关闭 buildOptimizer 可能会增加您的包大小,但至少您将获得预编译大部分代码的好处。

【讨论】:

1.是否可以在 aot :D 和 2 中延迟加载 jit 编译器。我们如何“动态”执行连接到 templateHTML 的 js/ts(装饰组件类)?

以上是关于Angular 6/7 AOT:动态模板渲染 - 为模块加载 JitCompiler的主要内容,如果未能解决你的问题,请参考以下文章

为啥即使模板访问私有属性,Angular AOT 也会编译?

在Angular 2中渲染动态模板时如何提供@Input

Angular2:使用管道动态渲染模板

有没有办法为 Angular 1.5 组件动态渲染不同的模板

干货丨angular2 JIT and AOT

Angular模板简介