angular
January 23

Создание своей стратегии загрузки в Angular

Недавно наткнулся на собеседовании наткнулся на вопрос, как создать свою стратегию загрузки. В интернете много примеров о том, как это делается для NgModule приложений, но для standalone примеров совсем немного. Что-ж, разберем этот вопрос детальнее.

В Angular есть несколько стратегий загрузки модулей и компонентов, которые позволяют оптимизировать работу приложения, особенно в больших проектах. Всего разработчику доступно 3 стратегии:

  • NoPreloading. Отключает предзагрузку. Ленивая загрузка будет происходить только в момент перехода на соответствующий маршрут.
  • PreloadingAllModules. Предзагружает все ленивые модули и компоненты, как только основное приложение будет загружено. Загрузка происходит в фоновом режиме, не блокируя основную инициализацию.
  • RouterPreloader. Это механизм для создания кастомной стратегии предзагрузки, позволяющий вам определять, какие модули или компоненты должны быть предзагружены

Именно третья стратегия позволяет создать новую, для этого нужно класс реализующий интерфейс PreloadingStrategy:

import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, of } from 'rxjs';

export class CustomPreloadingStrategy implements PreloadingStrategy {
  preload(route: Route, load: () => Observable<any>): Observable<any> {
    // Предзагружаем только те модули, где указан параметр data.preload === true
    return route.data?.['preload'] ? load() : of(null);
  }
}

и сообщить приложению об имеющейся стратегии на момент загрузки приложения (main.ts) и зарегистрировать DI токен (можно так же через providedIn: 'root'):

import { provideRouter, withPreloading, RouterPreloader } from '@angular/router';

bootstrapApplication(AppComponent, {
  providers: [
    { provide: RouterPreloader, useClass: CustomPreloadingStrategy }, // регистрируем токен
    provideRouter(routes, withPreloading(CustomPreloadingStrategy)) // передаём в роутер стратегию
  ],
});

Остаётся прописать флаг в роутингах, чтобы Angular знал что стоит предзагружать - а что нет.

const routes: Routes = [
  {
    path: 'important',
    loadChildren: () => import('./important/important.module').then(m => m.ImportantModule),
    data: { preload: true }, // Будет предзагружен
  },
  {
    path: 'optional',
    loadChildren: () => import('./optional/optional.module').then(m => m.OptionalModule),
    data: { preload: false }, // Не будет предзагружен
  },
];