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/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 { }
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.
更新:
创建了自定义包装器 CustomNgModule
、CustomComponent
和 CustomInjectable
装饰器:
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';
});
}
...
在 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/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 { }
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.
更新:
创建了自定义包装器 CustomNgModule
、CustomComponent
和 CustomInjectable
装饰器:
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 inAOT
withDI
functionality, replace native decorators (NgModule/Injectable...
) with custom one to preserve decorators inAOT
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';
});
}
...