使用Angular CLI& amp;在运行时动态加载新模块Angular 5 [英] Load new modules dynamically in run-time with Angular CLI & Angular 5

查看:692
本文介绍了使用Angular CLI& amp;在运行时动态加载新模块Angular 5的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目前,我正在开发一个托管在客户端服务器上的项目。对于新的模块,无意重新编译整个应用程序。也就是说,客户端希望在运行时更新路由器/延​​迟加载的模块。我已经尝试了几件事,但我无法让它发挥作用。我想知道你们中是否有人知道我还能尝试什么或者我错过了什么。

Currently I'm working on a project which is being hosted on a clients server. For new 'modules' there is no intention to recompile the entire application. That said, the client wants to update the router/lazy loaded modules in runtime. I've tried several things out but I can't get it to work. I was wondering if any of you knows what I could still try or what I missed.

我注意到的一件事,我尝试过的大部分资源都使用了角度cli,在构建应用程序时,默认情况下由webpack捆绑到单独的块中。这似乎是合乎逻辑的,因为它使用了webpack代码拆分。但是如果在编译时模块还不知道怎么办(但是编译后的模块存储在服务器的某个地方)?捆绑不起作用,因为它找不到要导入的模块。并且使用SystemJS将在系统上找到时加载UMD模块,但也通过webpack捆绑在一个单独的块中。

One thing I noticed, most of the resources I tried, using angular cli, are being bundled into seperate chunks by webpack by default when building the application. Which seems logical as it makes use of the webpack code splitting. but what if the module is not known yet at compile time (but a compiled module is stored somewhere on a server)? The bundling does not work because it can't find the module to import. And Using SystemJS will load up UMD modules whenever found on the system, but are also bundled in a seperate chunk by webpack.

我已尝试过的一些资源;

Some resources I already tried;

  • dynamic-remote-component-loader
  • module-loading
  • Loading modules from different server at runtime
  • How to load dynamic external components into Angular application
  • Implementing a plugin architecture / plugin system / pluggable framework in Angular 2, 4, 5, 6
  • Angular 5 - load modules (that are not known at compile time) dynamically at run-time
  • https://medium.com/@nikolasleblanc/building-an-angular-4-component-library-with-the-angular-cli-and-ng-packagr-53b2ade0701e
  • Some several other relating this topic.

我已尝试并实施的一些代码,但此时不工作;

Some code I already tried and implement, but not working at this time;

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

Extending router with normal module.ts file

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

正常SystemJS导入UMD捆绑包

Normal SystemJS Import of UMD bundle

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)

Import external module, not working with 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

Use 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);
});

尝试加载使用汇总的模块

Tried loading a module made with rollup

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构建时将其转换为块)

Example with SystemJsNgModuleLoader only works when the Module is already provided as 'lazy' route in the RouterModule of the app (which turns it into a chunk when built with webpack)

我在StackOverflow上找到了很多关于此主题的讨论,并且提供的解决方案似乎非常适合加载模块/组件如果事先知道则动态。但没有一个适合我们的项目用例。请让我知道我仍然可以尝试或潜入。

I found a lot of discussion about this topic on StackOverflow here and there and provided solutions seem really good of loading modules/components dynamically if known up front. but none is fitting for our use case of the project. Please let me know what I can still try or dive into.

谢谢!

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

I've found; https://github.com/kirjs/angular-dynamic-module-loading and will give this a try.

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

UPDATE: I;ve created a repository with an example of loading modules dynamically using SystemJS (and using Angular 6); https://github.com/lmeijdam/angular-umd-dynamic-example

推荐答案

我遇到了同样的问题。据我所知,直到现在:

I was facing the same problem. As far as I understand it until now:

Webpack将所有资源放入一个包中并替换所有 System.import 使用 __ webpack_require __ 。因此,如果要使用SystemJsNgModuleLoader在运行时动态加载模块,则加载程序将在bundle中搜索模块。如果捆绑包中不存在该模块,则会出现错误。 Webpack不会向服务器询问该模块。这对我们来说是一个问题,因为我们想要在构建/编译时加载一个我们不知道的模块。
我们需要的是在运行时(懒惰和动态)为我们加载模块的加载器。在我的示例中,我使用的是SystemJS和Angular 6 / CLI。

Webpack puts all resources in a bundle and replaces all System.import with __webpack_require__. Therefore, if you want to load a module dynamically at runtime by using SystemJsNgModuleLoader, the loader will search for the module in the bundle. If the module does not exist in the bundle, you will get an error. Webpack is not going to ask the server for that module. This is a problem for us, since we want to load a module that we do not know at build/compile time. What we need is loader that will load a module for us at runtime (lazy and dynamic). In my example, I am using SystemJS and Angular 6 / CLI.


  1. 安装SystemJS:npm install systemjs -save

  2. 将其添加到angular.json:脚本:[node_modules / systemjs / dist / system.src.js]

app.component.ts

import { Compiler, Component, Injector, ViewChild, ViewContainerRef } from '@angular/core';

import * as AngularCommon from '@angular/common';
import * as AngularCore from '@angular/core';

declare var SystemJS;

@Component({
  selector: 'app-root',
  template: '<button (click)="load()">Load</button><ng-container #vc></ng-container>'
})
export class AppComponent {
  @ViewChild('vc', {read: ViewContainerRef}) vc;

  constructor(private compiler: Compiler, 
              private injector: Injector) {
  }

  load() {
    // register the modules that we already loaded so that no HTTP request is made
    // in my case, the modules are already available in my bundle (bundled by webpack)
    SystemJS.set('@angular/core', SystemJS.newModule(AngularCore));
    SystemJS.set('@angular/common', SystemJS.newModule(AngularCommon));

    // now, import the new module
    SystemJS.import('my-dynamic.component.js').then((module) => {
      this.compiler.compileModuleAndAllComponentsAsync(module.default)
            .then((compiled) => {
                let moduleRef = compiled.ngModuleFactory.create(this.injector);
                let factory = compiled.componentFactories[0];
                if (factory) {
                    let component = this.vc.createComponent(factory);
                    let instance = component.instance;
                }
            });
    });
  }
}

my-dynamic.component.ts

import { NgModule, Component } from '@angular/core';
import { CommonModule } from '@angular/common';

import { Other } from './other';

@Component({
    selector: 'my-dynamic-component',
    template: '<h1>Dynamic component</h1><button (click)="LoadMore()">LoadMore</button>'
})    
export class MyDynamicComponent {
    LoadMore() {
        let other = new Other();
        other.hello();
    }
}
@NgModule({
    declarations: [MyDynamicComponent],
    imports: [CommonModule],
})
export default class MyDynamicModule {}

other.component.ts

export class Other {
    hello() {
        console.log("hello");
    }
}

如你所见,我们可以告诉SystemJS什么模块已存在于我们的包中。所以我们不需要再次加载它们( SystemJS.set )。我们将在 my-dynamic-component 中导入的所有其他模块(在此示例中为其他)将从服务器在运行时。

As you can see, we can tell SystemJS what modules already exist in our bundle. So we do not need to load them again (SystemJS.set). All other modules that we import in our my-dynamic-component (in this example other) will be requested from the server at runtime.

这篇关于使用Angular CLI&amp; amp;在运行时动态加载新模块Angular 5的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆