使用Angular CLI和Angular 5在运行时动态加载新模块

JavaScript Webpack

卡卡西Pro小卤蛋

2020-03-24

目前,我正在研究一个托管在客户端服务器上的项目。对于新的“模块”,无意重新编译整个应用程序。也就是说,客户端希望在运行时更新路由器/延​​迟加载的模块我已经尝试了几种方法,但是无法正常工作。我想知道你们中的任何人知道我仍然可以尝试还是错过了什么。

我注意到的一件事是,在构建应用程序时,默认情况下,我尝试使用angular cli尝试的大多数资源都由webpack捆绑为单独的块。这似乎很合理,因为它利用了webpack代码拆分。但是,如果在编译时尚不知道该模块(但是已编译的模块存储在服务器上的某个位置),该怎么办?绑定不起作用,因为它找不到要导入的模块。使用SystemJS可以在系统上找到时加载UMD模块,但也可以通过webpack捆绑到单独的块中。

我已经尝试过的一些资源;

我已经尝试并实现了一些代码,但目前无法运行;

用普通的module.ts文件扩展路由器

  this.router.config.push({
    path: "external",
    loadChildren: () =>
      System.import("./module/external.module").then(
        module => module["ExternalModule"],
        () => {
          throw { loadChunkError: true };
        }
      )
  });

UMD捆绑包的普通SystemJS导入

System.import("./external/bundles/external.umd.js").then(modules => {
  console.log(modules);
  this.compiler.compileModuleAndAllComponentsAsync(modules['External'])
    .then(compiled => {
      const m = compiled.ngModuleFactory.create(this.injector);
      const factory = compiled.componentFactories[0];
      const cmp = factory.create(this.injector, [], null, m);
    });
});

导入外部模块,不适用于Webpack(Afaik)

const url = 'https://gist.githubusercontent.com/dianadujing/a7bbbf191349182e1d459286dba0282f/raw/c23281f8c5fabb10ab9d144489316919e4233d11/app.module.ts';
const importer = (url:any) => Observable.fromPromise(System.import(url));
console.log('importer:', importer);
importer(url)
  .subscribe((modules) => {
    console.log('modules:', modules, modules['AppModule']);
    this.cfr = this.compiler
      .compileModuleAndAllComponentsSync(modules['AppModule']);
    console.log(this.cfr,',', this.cfr.componentFactories[0]);
    this.external.createComponent(this.cfr.componentFactories[0], 0);
});

使用SystemJsNgModuleLoader

this.loader.load('app/lazy/lazy.module#LazyModule')
  .then((moduleFactory: NgModuleFactory<any>) => {
    console.log(moduleFactory);
    const entryComponent = (<any>moduleFactory.moduleType).entry;
    const moduleRef = moduleFactory.create(this.injector);

    const compFactory = moduleRef.componentFactoryResolver
      .resolveComponentFactory(entryComponent);
  });

尝试加载由汇总制作的模块

this.http.get(`./myplugin/${metadataFileName}`)
  .map(res => res.json())
  .map((metadata: PluginMetadata) => {

    // create the element to load in the module and factories
    const script = document.createElement('script');
    script.src = `./myplugin/${factoryFileName}`;

    script.onload = () => {
      //rollup builds the bundle so it's attached to the window 
      //object when loaded in
      const moduleFactory: NgModuleFactory<any> = 
        window[metadata.name][metadata.moduleName + factorySuffix];
      const moduleRef = moduleFactory.create(this.injector);

      //use the entry point token to grab the component type that 
      //we should be rendering
      const compType = moduleRef.injector.get(pluginEntryPointToken);
      const compFactory = moduleRef.componentFactoryResolver
        .resolveComponentFactory(compType); 
// Works perfectly in debug, but when building for production it
// returns an error 'cannot find name Component of undefined' 
// Not getting it to work with the router module.
    }

    document.head.appendChild(script);

  }).subscribe();

SystemJsNgModuleLoader的示例仅在应用程序的RouterModule中已将模块作为“惰性”路由提供时才起作用(使用webpack构建时将其转换为大块)

我在这里和那里的StackOverflow上找到了很多有关此主题的讨论,并且提供了动态加载模块/组件的解决方案(如果事先知道的话)看起来真的很不错。但没有一个适合我们的项目用例。请让我知道我仍然可以尝试或尝试的内容。

谢谢!

编辑:我发现;https://github.com/kirjs/angular-dynamic-module-loading,然后尝试一下。

更新:我创建了一个存储库,并带有使用SystemJS(和Angular 6)动态加载模块的示例;https://github.com/lmeijdam/angular-umd-dynamic-example

第3461篇《使用Angular CLI和Angular 5在运行时动态加载新模块》来自Winter(https://github.com/aiyld/aiyld.github.io)的站点

4个回答
HarryNear 2020.03.24

我相信,如果您使用webpack构建和运行主应用程序,则可以使用SystemJS来加载UMD捆绑包。我使用了使用ng-packagr构建动态插件/附加模块的UMD捆绑包的解决方案。这个github演示了描述的过程:https : //github.com/nmarra/dynamic-module-loading

DavaidJim 2020.03.24

我使用了具有Angular 6的库支持https://github.com/kirjs/angular-dynamic-module-loading解决方案来创建我在Github上共享的应用程序。根据公司政策,它需要脱机。关于示例项目源的讨论一结束,我将在Github上分享它!

更新:可以找到回购; https://github.com/lmeijdam/angular-umd-dynamic-example

达蒙 2020.03.24

是的,您可以通过在路由器中将它们称为模块来延迟加载模块。这是一个示例https://github.com/start-angular/SB-Admin-BS4-Angular-6

  1. 首先将要使用的所有组件耦合到一个模块中
  2. 现在,在路由器中引用该模块,Angular会将您的模块延迟加载到视图中。
猴子村村 2020.03.24

使用angular 6库完成此操作,然后汇总即可。我刚刚进行了试验,可以与主应用程序共享独立的角度AOT模块,而无需最后重建。

  1. 在将角度库设置angularCompilerOptions.skipTemplateCodegen为false并在构建库之后,您将获得模块工厂。
  2. 之后,使用如下汇总构建一个umd模块: rollup dist/plugin/esm2015/lib/plugin.module.ngfactory.js --file src/assets/plugin.module.umd.js --format umd --name plugin
  3. 在主应用程序中加载文本源umd包并与模块上下文进行评估
  4. 现在您可以从导出对象访问ModuleFactory

在这里https://github.com/iwnow/angular-plugin-example您可以找到如何使用独立构建和AOT开发插件

问题类别

JavaScript Ckeditor Python Webpack TypeScript Vue.js React.js ExpressJS KoaJS CSS Node.js HTML Django 单元测试 PHP Asp.net jQuery Bootstrap IOS Android