在Angular应用中动态包含另一个Webpack捆绑包

10

我正在开发一个与webpack捆绑在一起的Angular2应用程序。一切都很顺利。然而,这个捆绑好的应用程序将被独立地运送,并且在运行时,在引导应用程序之前,我需要将其他NgModules指定到我打包在应用程序中的模块中。基本上,可以将其视为包含在index.html中的两个包,当引导之后,包1进入包2。只有包2引导了一个应用程序。因此,我进行了一些实验,想与您分享一下(如果我采取了一种相当肮脏和完全错误的方法,请纠正我。尽管如此,如果这是唯一的方法,我不介意肮脏。)唯一的要求是在转译期间不应进行任何操作。要求是我可以在我的index.html中包括一个或多个包,并且它们可以在没有彼此先前了解的情况下连接到实际应用程序中。

我之所以首先要这样做,是因为我在一个平台上工作,其他开发人员可以通过安装扩展来挂接代码。这意味着我提供最初的包,它在编译期间完全不知道其他包的存在。在AngularJS中很容易实现,但是转向Angular2及以后版本后,这变得不那么容易了。

我使用以下方法成功延迟了应用程序的引导:

在main.ts中:

window["platformBrowserDynamicRef"] = platformBrowserDynamic();
window["appModule"] = AppModule;

这个工作得很好,当我手动调用bootstrap时,应用程序能够正常启动。 我创建了另一个bundle,作为另一个独立的应用程序也可以正常运行。

但是,当我尝试将它们通过像上面那样隐式地导入组件并引导应用程序时,会出现一个我不知道如何解决的错误。

window["app"].modulesImport.push(CommonModule);
window["app"].modulesImport.push(FormsModule);
window["app"].modulesImport.push(BrowserModule);
window["app"].modulesImport.push(NgGridModule);
window["app"].modulesImport.push(ReactiveFormsModule);
window["app"].modulesImport.push(BrowserAnimationsModule);
window["app"].modulesImport.push(HttpModule);

@NgModule({
  imports: window["app"].modulesImport,
  declarations: [
      DYNAMIC_DIRECTIVES,
      PropertyFilterPipe,
      PropertyDataTypeFilterPipe,
      LanguageFilterPipe,      
      PropertyNameBlackListPipe      
  ],
  exports: [
      DYNAMIC_DIRECTIVES,
      CommonModule,
      FormsModule,
      HttpModule
  ]
})
export class PartsModule {

    static forRoot()
    {
        return {
            ngModule: PartsModule,
            providers: [ ], // not used here, but if singleton needed
        };
    }
}

将模块放在隐式数组中运行良好,但是一旦我从另一个包中添加一个模块,就会出现以下错误:

Uncaught Error: Unexpected value 'ExtensionsModule' imported by the module 'PartsModule'. Please add a @NgModule annotation.
    at syntaxError (http://localhost:3002/dist/app.bundle.js:43864:34) [<root>]
    at http://localhost:3002/dist/app.bundle.js:56319:44 [<root>]
    at Array.forEach (native) [<root>]
    at CompileMetadataResolver.getNgModuleMetadata (http://localhost:3002/dist/app.bundle.js:56302:49) [<root>]
    at CompileMetadataResolver.getNgModuleSummary (http://localhost:3002/dist/app.bundle.js:56244:52) [<root>]
    at http://localhost:3002/dist/app.bundle.js:56317:72 [<root>]
    at Array.forEach (native) [<root>]
    at CompileMetadataResolver.getNgModuleMetadata (http://localhost:3002/dist/app.bundle.js:56302:49) [<root>]
    at CompileMetadataResolver.getNgModuleSummary (http://localhost:3002/dist/app.bundle.js:56244:52) [<root>]
    at http://localhost:3002/dist/app.bundle.js:56317:72 [<root>]
    at Array.forEach (native) [<root>]
    at CompileMetadataResolver.getNgModuleMetadata (http://localhost:3002/dist/app.bundle.js:56302:49) [<root>]
    at JitCompiler._loadModules (http://localhost:3002/dist/app.bundle.js:67404:64) [<root>]
    at JitCompiler._compileModuleAndComponents (http://localhost:3002/dist/app.bundle.js:67363:52) [<root>]

我还尝试了对ExtensionsModule进行AoT编译,但也没有成功。同时,我尝试使用JitCompiler在运行时动态编译组件,以查看是否可以通过这种方式将它们引入应用程序。不过,出现了相同的错误。

组件是在应用程序引导期间还是在转译期间被装饰的?查看隐式数组中的模块,我可以看到它们都被装饰了,但我的模块没有。这是Webpack插入了装饰器方法的调用代码吗?我认为在转译期间装饰器信息会丢失,所以也许我需要暂时存储它,以便在其他地方获取它。(注意:作为第一个元素的extensionsmodule不包含装饰器数组)

modules that should be imported in my module

我想我正在寻找如何从这里继续前进的指针。或许NgModule注释函数是一个不错的起点。不过我无法在任何地方找到它。

编辑

深入研究了这个问题后,我尝试了使用路由器进行延迟加载的方法,但是在尝试使用js文件加载时出现了这个错误(网络选项卡显示它从未发出文件请求):

const appRoutes: Routes = [
      {
        path: 'edit-entity/:type/:id/:culture',
        component: EntityEditor,
        resolve: {
            entity: EntityResolver,
            navigation: NavigationResolver
        },
        data: { shouldDetach: true},
        loadChildren: "/dist/extensions.bundle.js#ExtensionsBundle",

      },
      {
        path: '',
        component: Dashboard,
        resolve: {
            navigation: NavigationResolver
        },
        data: { shouldDetach: true}        
      }
    ];

以下错误解决方案:

"Could not find module with name ExtensionsModule.js"

其次,我尝试使用如下的 loadChildren 函数:

const appRoutes: Routes = [
      {
        path: 'edit-entity/:type/:id/:culture',
        component: EntityEditor,
        resolve: {
            entity: EntityResolver,
            navigation: NavigationResolver
        },
        data: { shouldDetach: true},
        loadChildren: () => {
            //"/dist/extensions.bundle.js#ExtensionsModule",            
            // return System.import('./dynamic.module').then((comp: any) => {
            //     return comp.otherExport;
            // });

            return window["lux"].modulesLazyImport[0];
        }
      },
      {
        path: '',
        component: Dashboard,
        resolve: {
            navigation: NavigationResolver
        },
        data: { shouldDetach: true}        
      }
    ];

解决时间:

app.bundle.js:1548 ERROR Error: Uncaught (in promise): Error: No NgModule metadata found for 'ExtensionsModule'.
Error: No NgModule metadata found for 'ExtensionsModule'.
    at NgModuleResolver.resolve (app.bundle.js:55709) [angular]
    at CompileMetadataResolver.getNgModuleMetadata (app.bundle.js:56288) [angular]
    at JitCompiler._loadModules (app.bundle.js:67404) [angular]
    at JitCompiler._compileModuleAndComponents (app.bundle.js:67363) [angular]
    at JitCompiler.compileModuleAsync (app.bundle.js:67325) [angular]
    at ModuleBoundCompiler.compileModuleAsync (app.bundle.js:67693) [angular]
    at MergeMapSubscriber.project (app.bundle.js:30608) [angular]
    at MergeMapSubscriber._tryNext (app.bundle.js:69744) [angular]
    at MergeMapSubscriber._next (app.bundle.js:69734) [angular]
    at MergeMapSubscriber.Subscriber.next (app.bundle.js:14584) [angular]
    at ScalarObservable._subscribe (app.bundle.js:69206) [angular]
    at ScalarObservable.Observable.subscribe (app.bundle.js:118) [angular]
    at MergeMapOperator.call (app.bundle.js:69709) [angular]
    at Observable.subscribe (app.bundle.js:115) [angular]
    at NgModuleResolver.resolve (app.bundle.js:55709) [angular]
    at CompileMetadataResolver.getNgModuleMetadata (app.bundle.js:56288) [angular]
    at JitCompiler._loadModules (app.bundle.js:67404) [angular]
    at JitCompiler._compileModuleAndComponents (app.bundle.js:67363) [angular]
    at JitCompiler.compileModuleAsync (app.bundle.js:67325) [angular]
    at ModuleBoundCompiler.compileModuleAsync (app.bundle.js:67693) [angular]
    at MergeMapSubscriber.project (app.bundle.js:30608) [angular]
    at MergeMapSubscriber._tryNext (app.bundle.js:69744) [angular]
    at MergeMapSubscriber._next (app.bundle.js:69734) [angular]
    at MergeMapSubscriber.Subscriber.next (app.bundle.js:14584) [angular]
    at ScalarObservable._subscribe (app.bundle.js:69206) [angular]
    at ScalarObservable.Observable.subscribe (app.bundle.js:118) [angular]
    at MergeMapOperator.call (app.bundle.js:69709) [angular]
    at Observable.subscribe (app.bundle.js:115) [angular]
    at resolvePromise (app.bundle.js:77503) [angular]
    at resolvePromise (app.bundle.js:77488) [angular]
    at :3000/dist/app.bundle.js:77537:17 [angular]
    at Object.onInvokeTask (app.bundle.js:4599) [angular]
    at ZoneDelegate.invokeTask (app.bundle.js:77289) [angular]
    at Zone.runTask (app.bundle.js:77179) [<root> => angular]
    at drainMicroTaskQueue (app.bundle.js:77433) [<root>]
    at HTMLDivElement.ZoneTask.invoke (app.bundle.js:77364) [<root>]

编辑 我认为错误可能是因为我的注释从未被调用。不确定是否是这种情况。我决定在这上面使用ES5,并像下面一样将组件推送到我的隐式列表中。尝试将组件推送到parts.module.ts声明部分时,仍然会产生相同的错误。

    var HelloWorldComponent = function () {

};

HelloWorldComponent.annotations = [
    new ng.core.Component({
        selector: 'hello-world',
        template: '<h1>Hello World!</h1>',
    })
];

window["lux"].componentsLazyImport.push(HelloWorldComponent);

那么这是Angular 4.0.0中的一个bug还是根本不可能创建插件机制呢?

最好的问候 Morten


我正在研究与你相同的插件机制,并且遇到了和你一样的问题。四天过去了,我还没有找到任何解决方案。你有找到什么吗? - moohkooh
是的,我找到了一个答案,刚刚回复了它。 - Morten Skjoldager
可能是 在Angular2中将组件或模块动态加载到模块中的运行时 的重复问题。 - Morten Skjoldager
1个回答

1
我能找到的唯一实现方法是放弃使用 webpack,因为它似乎需要在构建时获取所有信息,而使用 SystemJS,它可以在运行时加载它不知道的模块。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接