如何将动态外部组件加载到 Angular 应用程序中

Posted

技术标签:

【中文标题】如何将动态外部组件加载到 Angular 应用程序中【英文标题】:How to load dynamic external components into Angular application 【发布时间】:2018-01-12 04:44:03 【问题描述】:

我遇到了 Angular 应用程序的问题。

我想要一个用 Typscript 编写的 Angular 应用程序,使用 (aot) 构建。

目的是显示带有一些小部件的用户仪表板。小部件是一个角度组件。

我的应用带有一些嵌入式小部件。 但是小部件应该通过市场之类的东西进行扩展;或手动创建。

市场应该将文件(js/ts/bunlde..??)下载到特定文件夹中。

然后我的应用应该能够加载新的小部件(= ng 组件)并实例化它们。

我的文件夹结构(生产)

  |- index.html
  |- main.f5b448e73f5a1f3f796e.bundle.js
  |- main.f5b448e73f5a1f3f796e.bundle.js.map
  |- .... (all other files generated)
  |- externalWidgets
      |- widgetA
            |- widjetA.js
            |- widjetA.js.map
            |- assets
                 |- image.png
      |- widgetB
            |- widjetB.ts
            |- widjetB.html
            |- widjetB.css

然后在加载用户页面时,数据库说有一个widgetA。 所以目标是动态加载文件并实例化包含的组件。

我尝试了很多解决方案,使用“require”和“System.import”,但在动态生成加载路径时都失败了。

这应该是可能的吗? 我可以改变我的代码结构;更改外部小部件..(例如,widgetB 尚未转译,...)

事实上,我正在寻找一个带有 Angular4/webpack 应用程序的“插件系统”。

【问题讨论】:

【参考方案1】:

我正在做同样的事情。我在 NgConf 的this talk 中解释了详细信息。

首先要了解的是,Webpack 不能在构建时动态加载未知的模块。这是 Webpack 构建依赖树并在构建时收集模块标识符的方式所固有的。这很好,因为 Webpack 是一个模块捆绑器,而不是模块加载器。所以你需要使用模块加载器,而现在唯一可行的选择是 SystemJS。

然后,每个插件都应该打包成一个模块,并且所有导出的组件都应该添加到该模块的entryComponents

在运行时,您将加载该模块以访问其内部声明的组件。你并不真的需要一个模块,但它是 Angular 中的一个打包单元,所以你不能避免使用它。现在,一旦你得到一个模块,你必须根据模块是否使用 AOT 构建来选择。

如果它是使用 AOT 构建的,你只需从模块中获取导出的工厂类并创建一个模块实例:

System.import('path/to/module').then((module)=>
    const moduleFactory = module.moduleFactoryClassName;
    const moduleRef = moduleFactory.create(this.parentInjector);
    const resolver = moduleRef.componentFactoryResolver;
    const compFactory = resolver.resolveComponentFactory(AComponent);

如果不是使用 AOT 构建的,则必须使用 JIT 编译器进行编译:

System.import('path/to/module').then((module)=>
    const moduleFactory = this.compiler.compileModuleSync(module.moduleClassName);
    const moduleRef = moduleFactory.create(this.parentInjector);
    const resolver = moduleRef.componentFactoryResolver;
    const compFactory = resolver.resolveComponentFactory(AComponent);

然后,您可以使用本文中描述的技术在任意位置添加动态组件:Here is what you need to know about dynamic components in Angular

【讨论】:

您的方法很有趣,是否可以使用这种方式导入远程托管的组件/模块? @Seb,是的,但是你需要使用 SystemJS 来做这个 太好了,可以为我解决问题!你有任何关于如何集成 systemJs 和 webpack 的例子吗?还有一个远程组件的 System.import 示例? 你使用 angular-cli 吗? 我在 github 上创建了一个存储库,其解决方案可能会有所帮助。它使用 Angular 6 个库和 1 个基础应用程序,懒惰地加载 UMD 捆绑库; github.com/lmeijdam/angular-umd-dynamic-example如果有什么建议,欢迎补充! PS:它远非完美,但它至少在运行时加载“未知”库。需要寻找“评估”替代方案

以上是关于如何将动态外部组件加载到 Angular 应用程序中的主要内容,如果未能解决你的问题,请参考以下文章

如何将第 3 方脚本从 Web 动态加载到 Angular2 组件中

如何在作为 innerHtml 加载的文档的子部分中动态加载 Angular 组件?

如何将动态模板加载到角度组件中

如何以角度动态删除组件

Angular 2 - 使用 ComponentResolver 加载在运行时下载的组件

将外部 CSS 加载到组件中