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

2023-11-26

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

我注意到的一件事是,在构建应用程序时,我使用 Angular cli 尝试过的大多数资源默认被 webpack 捆绑成单独的块。这看起来很合乎逻辑,因为它利用了 webpack 代码分割。但是如果模块在编译时尚不可知(但编译后的模块存储在服务器上的某个位置)怎么办?捆绑不起作用,因为找不到要导入的模块。只要在系统上找到 UMD 模块,使用 SystemJS 就会加载它们,但也会被 webpack 捆绑在单独的块中。

我已经尝试过一些资源;

  • 动态远程组件加载器
  • 模块加载
  • 在运行时从不同服务器加载模块
  • 如何将动态外部组件加载到 Angular 应用程序中
  • 在 Angular 2、4、5、6 中实现插件架构/插件系统/可插拔框架
  • Angular 5 - 在运行时动态加载模块(在编译时未知)
  • https://medium.com/@nikolasleblanc/building-an-angular-4-component-library-with-the-angular-cli-and-ng-packagr-53b2ade0701e
  • 其他一些与此主题相关的内容。

我已经尝试并实现了一些代码,但目前不起作用;

使用普通 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);
  });

尝试加载用 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();

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

我在 StackOverflow 上发现了很多关于这个主题的讨论,并且提供的解决方案似乎非常适合动态加载模块/组件(如果预先知道的话)。但没有一个适合我们的项目用例。请让我知道我仍然可以尝试或深入研究什么。

Thanks!

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

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


我面临着同样的问题。据我目前的理解:

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

  1. 安装SystemJS:npm install systemjs –save
  2. 将其添加到 angular.json: "scripts": [ "node_modules/systemjs/dist/system.src.js"]

应用程序组件.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 {}

其他组件.ts

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

正如你所看到的,我们可以告诉 SystemJS 我们的包中已经存在哪些模块。所以我们不需要再次加载它们(SystemJS.set)。我们导入的所有其他模块my-dynamic-component(在这个例子中other)将在运行时从服务器请求。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Angular CLI 和 Angular 5 在运行时动态加载新模块 的相关文章

随机推荐

  • django-compressor 是否支持模板继承?

    我在用着Django 压缩器压缩我网站的静态 CSS 和 Javascript 文件 由于我通过 Amazon S3 提供网站的静态资产 因此我还使用Django 存储将我的文件上传到 S3 这是我的问题 我正在努力清理base html我
  • 强制 attr=title 弹出 on 元素

    有没有办法 我可以强制元素在元素悬停时显示 标题 弹出窗口 或者如果没有 有没有办法 我可以配置显示标题弹出窗口之前的超时时间 默认情况下 标题在悬停时显示 您无法更改其行为
  • Javascript 或 Flash 导出至 CSV/Excel

    是否有办法将 JSON 数据导出到 CSV Excel 而无需与服务器端进行任何交互 仅使用 JavaScript 还是闪存 我目前正在使用 ZeroClipboard 将值复制到剪贴板 但我想从浏览器 FF Chrome IE 等 直接将
  • net/http.rb:560:in `initialize': getaddrinfo: 名称或服务未知(SocketError)

    timestamp nil def generate oauth url timestamp timestamp url CONNECT URL REQUEST TOKEN PATH oauth callback OAUTH CALLBAC
  • 带有 v-for 的动态 v 模型

    我有一个 v for 循环 它将吐出多行输入 我想将每个单独的行动态保存到数组对象中 v for table class table m 0 tbody tr td fund name td tr tbody table
  • 来自电子邮件地址的域的正则表达式

    任何人都可以帮助我使用正则表达式来返回电子邮件地址的末尾部分 符号之后 吗 我是正则表达式的新手 但想学习如何使用它 而不是编写低效的 Net 字符串函数 例如 对于输入 电子邮件受保护 我需要 example com 的输出 干杯 蒂姆
  • $q.reject 和处理 AngularJS 链式承诺中的错误

    我无法理解使用链接承诺进行错误处理的基本概念 为了学习规则 我写了一个简单的例子 猜测结果会是什么 但不幸的是 它的行为并不像我想象的那样 我已经阅读了多篇有关该主题的文章 但由于我的英语水平不佳 我可能无法获得详细信息 无论如何 这是我的
  • Python 文件变量 - 它是什么?

    我刚刚开始使用 Python 由于我的背景是低级语言 java C 所以我无法真正理解一些东西 因此 在 python 中 我们可以通过打开一个文本文件来创建一个文件变量 然后像这样迭代它的行 f open sys argv 1 for l
  • 正则表达式匹配任何大于 1 的整数

    我最近刚刚开始学习正则表达式 我正在尝试找出如何匹配任何大于 1 的数字的模式 到目前为止我想出了 2 9 0 9 但它仅适用于最左边的数字不为 1 的情况 例如 234有效但是124没有 所以我想要实现的是个位数1不应匹配任何大于应匹配的
  • Postgres 是否支持嵌套或自治事务?

    我遇到的情况是 我必须将一部分代码作为其自己的事务提交 我创建了一个表subtransaction tbl CREATE TABLE subtransaction tbl entryval integer 以及 plpython3u 语言中
  • 如何在 Mac OSX 上安装 PCRE 开发标头

    我刚刚将 MacBook Pro 升级到 Mavericks 当我访问时 我本地的 Ruby on Rails 开发环境并没有立即运行localhost I see It works 并记得我需要启动 Phusion Passenger 所
  • 特征列嵌入查找

    我一直在使用tensorflow中的数据集和feature columns https developers googleblog com 2017 11 introducing tensorflow feature columns htm
  • ModuleNotFoundError:没有名为“pyperclip”的模块

    类似的问题已经发布在 StackOverflow 上 但我没有找到足够的答案来解决这个问题 我在 Windows 7 计算机上运行 Python 3 6 3 在 IDLE 中 我输入以下 import stmt 并收到后续错误 gt gt
  • 如何在VB 2010中通过文件的默认应用程序打开选定的文件?

    我有用 VB 2010 编写的 Windows 应用程序 在这里 用户可以从打开的对话框中选择任何文件 所以 我想在相应的应用程序中打开该文件 例如 假设用户选择 docx 文件 那么我必须使用 msword 打开该文件 假设 如果它是一个
  • 排序哈希表(映射、字典)数据结构设计

    下面是数据结构的描述 它的操作就像一张普通的地图get put and remove方法 但有一个sort可以调用对地图进行排序的方法 然而 地图记得它的排序结构 因此后续调用 sort 可以更快 如果结构在调用之间没有改变太多 sort
  • ImageMagick 转换和 GNU 并行在一起

    我想加快以下命令的速度 convert limit memory 64 limit map 128 antialias delay 1x2 final png movie mp4 我见过其他的博客文章其中并行和转换一起使用 所以我想知道如何
  • 从多个选择列表中获取未选择的选项

    我有一个多选列表 当用户取消选择所选选项时 我想知道用户取消选择的选项的值 我该如何捕捉它 我的示例代码如下
  • 动态地将一组字段添加到反应式表单中

    我有两个输入字段 姓名和姓氏 我有两个按钮 提交和 添加人员 单击 添加人员 应添加一组新字段 姓名 如何实现这一目标 我找到了如何动态添加单个输入字段的解决方案 但在这里我需要添加一组 我的代码现在没有 添加人员 功能 import Fo
  • 为什么在 Windows 上尝试 Socket.connect 失败需要 1 秒?

    使用 net 时 我注意到尝试连接到未侦听的端口总是需要 1 秒 检查这是否是 net 抽象中的问题或者是否是较低级别的问题 并将其与 linux 进行比较 其中不成功telnet大约需要 3 毫秒 我使用了一个 node js 脚本来连接
  • 使用 Angular CLI 和 Angular 5 在运行时动态加载新模块

    目前我正在开发一个托管在客户端服务器上的项目 对于新的 模块 有无意重新编译整个应用程序 也就是说 客户想要在运行时更新路由器 延 迟加载模块 我已经尝试了几种方法 但无法使其发挥作用 我想知道你们中是否有人知道我还可以尝试什么或我错过了什