【问题标题】:How to manually lazy load a module?如何手动延迟加载模块?
【发布时间】:2017-03-10 15:32:12
【问题描述】:

我尝试使用SystemJsNgModuleLoader 加载不带路由器的模块,但无法正常工作:

this.loader.load(url).then(console.info);

对于我用于 URL 的任何字符串(绝对/相对 urls/路径...尝试了很多选项),我都会得到 Cannot find module xxx。我查看了路由器源代码,除了这个SystemJsNgModuleLoader 之外找不到任何东西。我什至不确定我应该使用这个...


昨天在ng-europe 2016 会议上提出了这个问题 - Miško 和 Matias 回答:

米什科·赫弗里: 只需获取模块,从那里您可以获取组件工厂,并且您可以在应用程序中的任何位置动态加载组件工厂。这正是路由器内部所做的。所以你也很难做到这一点。

马蒂亚斯·尼梅莱 唯一需要注意的是,在 [Ng]Module 上有一个名为 entryComponents 的东西,它标识了可以延迟加载的组件——这是该组件集的入口。所以当你有延迟加载的模块时,请将这些东西放入entryComponents

...但是如果没有关于该主题的示例和糟糕的文档(;

任何人都知道如何手动加载模块,而不使用Route.loadChildren?如何获取模块以及应该进入entryComponents内容到底是什么(我读过FAQ,但如果不实际加载模块就无法尝试)?

【问题讨论】:

标签: angular


【解决方案1】:

任何人都知道如何手动加载模块,而无需使用 Route.loadChildren?

您可以使用SystemJsNgModuleLoader 获取模块的工厂:

this.loader.load('./src/lazy.module#TestModule').then((factory: NgModuleFactory<any>) => {
  console.log(factory);
});

对于 Angular 8,请参阅 Lazy load module in angular 8

下面是它的样子:

lazy.module.ts

@Component({
  selector: 'test',
  template: `I'm lazy module`,
})
export class Test {}

@NgModule({
  imports: [CommonModule],
  declarations: [Test],
  entryComponents: [Test]
})
export class LazyModule {
  static entry = Test;
}

app.ts

import {
  Component, NgModule, ViewContainerRef,
  SystemJsNgModuleLoader, NgModuleFactory,
  Injector} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'

@Component({
  selector: 'my-app',
  template: `<h2>Test lazy loading module</h2>`,
})
export class AppComponent {
  constructor(
    private loader: SystemJsNgModuleLoader, 
    private inj: Injector, 
    private vcRef: ViewContainerRef) {}

  ngOnInit() {
     this.loader.load('./src/lazy.module#LazyModule')
       .then((moduleFactory: NgModuleFactory<any>) => {
         const moduleRef = moduleFactory.create(this.inj);
         const entryComponent = (<any>moduleFactory.moduleType).entry;
         const compFactory = 
               moduleRef.componentFactoryResolver.resolveComponentFactory(entryComponent);
         this.vcRef.createComponent(compFactory);
      });
  }
} 

@NgModule({
  imports: [ BrowserModule ],
  declarations: [ AppComponent ],
  providers: [SystemJsNgModuleLoader],
  bootstrap: [ AppComponent ]
})
export class AppModule {} 
this.loader.load('./src/test.module#TestModule').then((factory: NgModuleFactory<any>) => {
  console.log(factory);
});

Plunker Example

AOT预编译模块有两种选择:

1) Angular CLIlazyModules 选项(从 Angular 6 开始)

使用 angular/cli 内置功能:

{
  "projects": {
    "app": {
      "architect": {
        "build": {
          "options": {
            "lazyModules": [  <====== add here all your lazy modules
              "src/path-to.module"
            ]
          }
        }
      }
    }
  }
} 

2) 使用 RouterModule 中的 provideRoutes

app.module.ts

providers: [
  SystemJsNgModuleLoader,
  provideRoutes([
     { loadChildren: 'app/lazy/lazy.module#LazyModule' }
  ])
],

app.component.ts

export class AppComponent implements  OnInit {
    title = 'Angular cli Example SystemJsNgModuleLoader.load';

    @ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef;

    constructor(private loader: SystemJsNgModuleLoader, private inj: Injector) {}

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

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

Github repo angular-cli-lazy


使用 webpack 和 AOT 进行延迟加载

使用 ngc 编译

使用以下工厂初始化编译器

export function createJitCompiler () {
    return new JitCompilerFactory([{useDebug: false, useJit: true}]).createCompiler();
}

Github repo

【讨论】:

  • 让它工作 - 由于我正在从另一个功能模块加载一个功能模块(src/app/one/one.module 加载 src/app/two/two.module,路径应该是 @987654338,因此找出确切路径有点棘手@)... 通过挖掘 SystemJsNgModuleLoader._compiler._ngModule.Scopes[0].Closure.routes.path 来解决这个问题,它是空字符串,而不是我预期的 ./app/one(:您的 plunker 示例有所帮助。再次感谢!
  • @skusunam 我为 angular-cli 添加了示例
  • @yurzui 我与 SystemJsNgModuleLoader.load 有类似的问题,我找不到模块。我正在尝试将另一个 webpack umd 包的模块加载为 loader.load("app2/lib/app2.bundle.js#myModule")
  • 找到了解决方案,但我不知道它是否是正确的解决方案。为了让 webpack 创建块,我必须包含一个延迟加载的路由。这意味着我只需要为此目的提供可访问的路线。
  • @swingmicro 你找到解决方案了吗?从外部 url(umd 库)加载模块?
【解决方案2】:

[Angular 6]

你好,

我在这里分享我的解决方案,因为我没有找到如何在 stackoverflow 上在没有路由器的情况下延迟加载

Yurzui的方法可行,但他使用Router模块编译惰性模块,而我不想使用它。

在我们的src/angular.json 文件中,我们可以要求@angular/cli 分开编译模块。

为此,我们在 "project" > "your-app-name" > "architect" > "build" > "options" 中添加 lazyModules 键。

像这样:

  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects", 
  "projects": {
    "lazy-load-app": {
      "root": "",
      "sourceRoot": "src",
      "projectType": "application",
      "prefix": "app",
      "schematics": {},
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/lazy-custom-element-app",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.app.json",
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.css"
            ],
            "scripts": [],
            "lazyModules": [
              "src/app/lazy-load/lazy-load.module",
              "src/app/another-lazy-load/another-lazy-load.module"
            ]

然后我们可以调用并加载我们编译的模块。

像这样:

export class LoaderComponent implements OnInit {

      // tag where we gonna inject the lazyload module and his default compononent "entry"
      @ViewChild('container', { read: ViewContainerRef }) viewRef: ViewContainerRef;

      constructor(
        private loader:     NgModuleFactoryLoader,
        private injector:   Injector,
        private moduleRef:  NgModuleRef<any>,) {
      }

      ngOnInit(): void {
       this.loader.load(this.pathModuleTemplate).then((moduleFactory: NgModuleFactory<any>) => {
          // our module had a static property 'entry' that references the component that  want to load by default with the module
          const entryComponent = (<any>moduleFactory.moduleType).entry;
          const moduleRef = moduleFactory.create(this.injector);
          const compFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(entryComponent);
          this.viewRef.createComponent(compFactory);
        });
      }
}

来源:https://github.com/angular/angular-cli/blob/9107f3cc4e66b25721311b5c9272ec00c2dea46f/packages/angular_devkit/build_angular/src/server/schema.json

希望它可以帮助某人:)

【讨论】:

  • 很好,this.pathModuleTemplate 可以是服务器上模块的 URL 吗?
  • 抱歉迟到了。我不这么认为,但我从未尝试过,而且我显然不是专家。如果你尝试它,告诉我它是否有效,我很好奇:)
  • 感谢模块已加载,但模块“路由模块”内的路由无法加载且无法识别,我们如何从惰性模块加载路由?
  • 我认为你必须使用 RouterModule 中的 forChild 方法。 angular.io/guide/lazy-loading-ngmodules *转到“配置功能模块的路由”
猜你喜欢
  • 1970-01-01
  • 2018-05-22
  • 2017-02-22
  • 1970-01-01
  • 2021-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多