angular 同一项目的 AOT 和 JIT

angular AOT and JIT on same project

在 angular5 上,我尝试对我的大部分 module/component 进行同一个项目的 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 的 "compiler" 部分。 我有什么尝试 - 我已经尝试直接在我的代码中要求它,但仍然不存在。 - 我已经尝试检查像 angular(或 angular material)这样的网站如何处理它。但不适合我的情况。事实上,两者都已经在AOT版本中编译了所有示例的版本。动态部分是样本周围的 "just" 内容。

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

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

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


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

如您所见,AOT 编译从 bundle 中删除了 wall 编译器,结果:

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

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

Afaik 这不能(现在)完成,因为 angular removes metadata on production build

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

试试这个:

    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

But JitCompiler still not able to create Dependency Injection tree. I suspect @Injectable to be remove from AOT part. But i can't do your trick.

在上面的代码示例中,NgModule 和 Component 没有 decorators。所以,这意味着也没有 @Injectable,他们不能注入 providers。那么为什么我们不写 @NgModule@Component @Injectable 装饰器而只写它服务? 因为,他们有装饰器(@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);
  };
}

When building with AOT flag, Angular-CLI looks like cleans bundle from native decorators from parts of code which need to be compiled dynamically.

And where you want dynamically compile modules with components in AOT with DI functionality, replace native decorators (NgModule/Injectable...) with custom one to preserve decorators in AOT compilation mode:

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