Angular2(CLI)摇树删除动态创建的 NgModule
Posted
技术标签:
【中文标题】Angular2(CLI)摇树删除动态创建的 NgModule【英文标题】:Angular2 (CLI) tree shaking removing dynamically created NgModule 【发布时间】:2017-07-21 02:23:32 【问题描述】:我认为Angular-cli tree-shaking exclude component from removal 的问题非常相似,但我似乎无法从中得到什么。
基本上我有一个动态组件工厂,如How can I use/create dynamic template to compile dynamic Component with Angular 2.0? 中所述。
当我使用带有非生产设置的最新 Angular CLI 构建它时,一切正常。但是,一旦我使用生产设置,当我尝试加载具有动态创建内容的页面时,我会立即在浏览器中看到以下错误跟踪:
异常:未找到“e”的 NgModule 元数据。 原始堆栈跟踪: main.dc05ae9….bundle.js:formatted:4731 错误:没有 NgModule 元数据 为“e”找到。 在 f (vendor.c18e6df….bundle.js:formatted:76051) 在 t.resolve (vendor.c18e6df….bundle.js:formatted:20624) 在 t.getNgModuleMetadata (vendor.c18e6df….bundle.js:formatted:20169) 在 t._loadModules (vendor.c18e6df….bundle.js:formatted:40474) 在 t._compileModuleAndAllComponents (vendor.c18e6df….bundle.js:formatted:40462) 在 t.compileModuleAndAllComponentsSync (vendor.c18e6df….bundle.js:formatted:40436) 在 e.createComponentFactory (main.dc05ae9….bundle.js:formatted:4789)
这是我的组件工厂类:
@Injectable()
export class DynamicTypeBuilder
constructor()
private _cacheOfFactories: [templateKey: string]: ComponentFactory<any> = ;
private compiler: Compiler = new JitCompilerFactory([useDebug: false, useJit: true]).createCompiler();
public createComponentFactory<COMPONENT_TYPE>(type: any, template: string, additionalModules: any[] = []): Observable<ComponentFactory<COMPONENT_TYPE>>
let factory = this._cacheOfFactories[template];
if (factory)
return Observable.of(factory);
// unknown template ... let's create a Type for it
let module = this.createComponentModule(type, additionalModules);
// compiles and adds the created factory to the cache
return Observable.of(this.compiler.compileModuleAndAllComponentsSync(module))
.map((moduleWithFactories: ModuleWithComponentFactories<COMPONENT_TYPE>) =>
factory = moduleWithFactories.componentFactories.find(value => value.componentType == type);
this._cacheOfFactories[template] = factory;
return factory;
);
protected createComponentModule(componentType: any, additionalModules: any[]): Type<any>
@NgModule(
imports: [
FormsModule,
ReactiveFormsModule,
BrowserModule,
PipesModule,
...additionalModules
],
declarations: [
componentType
],
schemas:[CUSTOM_ELEMENTS_SCHEMA]
)
class RuntimeComponentModule
return RuntimeComponentModule;
正在被转译为
var _ = function()
function e()
this._cacheOfFactories = ,
this.compiler = new i.a([
useDebug: !1,
useJit: !0
]).createCompiler()
return e.prototype.createComponentFactory = function(e, t, n)
var i = this;
var _ = this._cacheOfFactories[t];
if (_)
r.Observable.of(_);
var a = this.createComponentModule(e, n);
return r.Observable.of(this.compiler.compileModuleAndAllComponentsSync(a)).map(function(n)
return _ = n.componentFactories.find(function(t)
return t.componentType == e
),
i._cacheOfFactories[t] = _,
_
)
,
e.prototype.createComponentModule = function(e, t)
var n = function()
function e()
return e
();
return n
,
e.ctorParameters = function()
return []
,
e
()
错误消息中的“e”是来自createComponentModule
的函数e()
,正如您所见,它是空的,即使它应该包含@NgModule
内容。
如何动态创建一个新的 NgModule 并且仍然使用 Angular CLI 的生产模式?
版本: Angular2:2.4.8 Angular CLI:1.0.0-beta.32.3 打字稿:2.1.6
【问题讨论】:
请更新您的问题,因为它需要一些清晰度 【参考方案1】:我有同样的错误信息。我发现的解决方法是不要对运行时模块使用装饰器。
protected createComponentModule(componentType: any, additionalModules: any[]): Type<any>
return NgModule(
imports: [
FormsModule,
ReactiveFormsModule,
BrowserModule,
PipesModule,
...additionalModules
],
declarations: [
componentType
],
schemas:[CUSTOM_ELEMENTS_SCHEMA]
)(class RuntimeComponentModule );
好的,我没有完全理解为什么会发生错误。错误消息基本上说模块e
没有元数据。 Angular 中模块的元数据通常被声明为装饰器。
ES7 中的装饰器等价于 curry 函数。这意味着
@NgModule()
class A
等于
NgModule()(class A )
我个人认为咖喱的方式要好得多...
更新了 22 场比赛:
来自官方repo的答案https://github.com/angular/angular-cli/issues/5359#issuecomment-287500703
根本不使用AOT。
请使用ng build --prod --no-aot
构建代码
就我而言,一切都解决了。
【讨论】:
恐怕不能正常工作。代码编译良好,并包含在转译的 JS 中,但是,现在有一个新问题。 Angular 在新的NgModule
中找不到任何自定义导入的模块。所以在上面的例子中FormsModule
、ReactiveFormsModule
和BrowserModule
可以找到,但是PipesModule
不能。错误在CompileMetadataResolver.getNgModuleMetadata
。
其实我也有同样的问题。这似乎在 JIT 编译中根本没有编译装饰器。不仅RuntimeComponentModule
。如果您阅读编译后的main.js
,它不包含PipesModule
的元数据。我将继续这个tmr。谢谢你告诉我角度模块很好。【参考方案2】:
不幸的是,无论是 Angular 2.x 还是 Angular 4 beta,目前这似乎都是不可能的(我会尽量保持最新的答案)。 问题在于动态组件定义包含文件引用(模板、样式表),在运行时无法再通过之前运行的 AOT 编译器解决这些引用。 但是,如果组件或模块不包含文件引用,当前的 Angular 代码也不允许真正动态地创建组件。它只是找不到在运行时创建的元数据。
总结问题,动态组件创建分为3个层次:
-
静态定义一个组件并将其包含在一个 NgModule 中,AOT 编译器可以在 AOT 编译时找到它。这样的组件可以在任何时候实例化而不会出现问题。 (参见 ComponentFactoryResolver 等)
静态定义组件主体(代码等),但允许具有动态模板和/或样式(即,仅在需要时在代码中创建模板)。这也需要在运行时编译 NgModule。目前只有在不使用 AOT 编译器时才有可能,这代表了我在此处发布的问题。
动态定义完整的组件,包括代码和模板。这不是这里的意图,甚至可能会走得很远。但可能有人也有这个问题。
在我看来,第二个问题是可以解决的。 Angular 团队说,因为它是 AOT,它只能编译那些在 AOT 编译时静态已知的东西,但我不同意。
我可以考虑 AOT 编译此类组件的“存根”的可能性,然后在需要时使用动态模板或样式表对其进行实例化。可能需要为 @Component
注释使用新属性或像 @DynamicComponent
这样的全新注释,但对我来说似乎可行。我不知道@NgModule
声明是否需要进行相同的更改,但我认为他们会这样做。
【讨论】:
在 v4 中我正在使用它。一次 jit + aot :) 是的,在 github 上看到了你的帖子!现在我们需要让它在 CLI 中运行。 ciao 塞巴斯蒂安! @PatrikLaszlo 你能指导我们吗? pages.corifeus.com/github/corifeus-builder-angular/artifacts/…以上是关于Angular2(CLI)摇树删除动态创建的 NgModule的主要内容,如果未能解决你的问题,请参考以下文章