Angular lazyload module без route

Ленивая загрузка (по мере необходимоси) помогает уменьшить начальные размеры js файлов, что, в свою очередь, помогает сократить время загрузки. В Angular основной способ загружать модули лениво - настроить их загрузку в зависимости от выбранного маршрута.

Используется Angular 8

const routes: Routes = [
  {
    path: 'customers',
    loadChildren: () => import('./customers/customers.module').then(mod => mod.CustomersModule)
  },
  {
    path: 'orders',
    loadChildren: () => import('./orders/orders.module').then(mod => mod.OrdersModule)
  },
];

Но что, если хочется управлять этим механизмом более гибко?

CLI-based lazy modules

Такая возможность есть в angular CLI. Предствим, что имеется модуль src\app\lazy\lazy.module.ts с 3 компонентами.

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

import { OneComponent } from './one/one.component';
import { TwoComponent } from './two/two.component';
import { EntryComponent } from './entry/entry.component';

@NgModule({
  declarations: [OneComponent, TwoComponent, EntryComponent],
  entryComponents: [EntryComponent],
  imports: [CommonModule],
})
export class LazyModule {
  static rootEntry = EntryComponent;
}

Нужные компоненты для динамической отрисовки должны находится в массиве entryComponents

В файле настроек проекта angular.json следуется поместить созданные модуль в массив lazyModules (projects["<name-of-project>"].architect.build.options.lazyModules)

...
"options": {
    "outputPath": "dist/lazy-module-no-route",
    "index": "src/index.html",
    "main": "src/main.ts",
    "polyfills": "src/polyfills.ts",
    "tsConfig": "tsconfig.app.json",
    "aot": false,
    "lazyModules": ["src/app/lazy/lazy.module"],  <<=== вот сюда
    "assets": [
      "src/favicon.ico",
      "src/assets"
    ],
    "styles": [
      "src/styles.css"
    ],
    "scripts": []
},
...

Ну и немного магии: src\app\app.component.ts

@Component({
  selector: 'app-root',
  template: `<h1>Welcome!</h1><div #here></div>`,
})
export class AppComponent implements AfterViewInit, OnDestroy {
  /** Экземпляр Модуля созданного через фабрику */
  moduleRef: NgModuleRef<LazyModule>;
  /** Контейнер для размещения динамического компонента */
  @ViewChild('here', { read: ViewContainerRef, static: true })
  here: ViewContainerRef;

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

  ngAfterViewInit(): void {
    import('src/app/lazy/lazy.module')
      .then(m => m.LazyModule)
      .then(lazyModule => {
        this.compiler.compileModuleAsync(lazyModule).then(ngModuleFactory => {
          this.moduleRef = ngModuleFactory.create(this.injector);
          const compFactory = this.moduleRef.componentFactoryResolver.resolveComponentFactory(
            lazyModule.rootEntry
          );
          this.here.createComponent(compFactory);
        });
      });
  }

  ngOnDestroy(): void {
    if (this.moduleRef) {
      this.moduleRef.destroy();
    }
  }
}
  • Загрузить модуль LazyModule
  • Скомпилировать загруженный модуль
  • Создать фабрику LazyModule модулей
  • Из фабрики создать экземпляр модуля
  • Из экземпляра модуля создать фабрику нужного компонента
  • Из фабрики компонента создать компонент
  • Прицепить компонент к какому-нибудь ViewContainerRef
  • Изи?

Пример на github


Похожие записи

RxJs Subjects

Выдержки из доклада Андрея Алексеева (Tinkoff) про RxJs (Subject, Behaviour Subject, Replay Subject, Async Subject). Применение в Angular.

Angular URL Matcher

Функция сопоставления маршрута с URL-адресами. Возможность динамически подбирать компонент для маршрута

04 октября 2020 г. в Angular