Как lazy loading реализуется в Angular?
Lazy loading (ленивая загрузка) в Angular — это механизм, позволяющий загружать модули приложения только по мере необходимости, а не при первоначальной загрузке. Это существенно уменьшает размер начального JavaScript-бандла, снижает время первого отображения страницы (TTI) и повышает производительность приложения, особенно в крупных проектах с множеством маршрутов.
Основная идея
Angular разбивает приложение на feature-модули, каждый из которых может загружаться по требованию, а не сразу при запуске. Такой модуль подключается только при активации соответствующего маршрута.
Этапы реализации lazy loading
1. Создание модуля с маршрутизацией
Создайте отдельный модуль, например, AdminModule:
ng generate module admin --route admin --module app.module
Команда создаст:
-
admin.module.ts — модуль
-
admin-routing.module.ts — маршрутизация
-
Добавит ленивую загрузку в app-routing.module.ts
Либо вручную:
#### **admin.module.ts**
@NgModule({
declarations: \[AdminComponent\],
imports: \[CommonModule, AdminRoutingModule\]
})
export class AdminModule {}
#### **admin-routing.module.ts**
const routes: Routes = \[
{ path: '', component: AdminComponent }
\];
@NgModule({
imports: \[RouterModule.forChild(routes)\],
exports: \[RouterModule\]
})
export class AdminRoutingModule {}
2. Настройка ленивой загрузки в app-routing.module.ts
const routes: Routes = \[
{
path: 'admin',
loadChildren: () =>
import('./admin/admin.module').then(m => m.AdminModule)
}
\];
Что делает loadChildren
-
Загружает admin.module.ts только тогда, когда пользователь перейдёт по маршруту /admin.
-
Использует динамический импорт ES2020: import().then() — это ключ к ленивай загрузке.
-
Браузер получает новый JS-чанк (bundle), содержащий код AdminModule.
Важные особенности
Структура лениво загружаемого модуля
Ленивые модули должны использовать RouterModule.forChild() вместо forRoot():
RouterModule.forChild(routes)
forRoot() используется только один раз — в AppRoutingModule.
Компоненты в ленивом модуле не должны использоваться напрямую в AppComponent, иначе Angular будет вынужден загрузить модуль сразу. Ленивый модуль должен быть полностью изолирован.
Пример ленивой загрузки нескольких модулей
const routes: Routes = \[
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)
},
{
path: 'user',
loadChildren: () => import('./user/user.module').then(m => m.UserModule)
}
\];
Каждый модуль admin и user будет загружен только при переходе к соответствующему маршруту.
Предзагрузка модулей (preloading)
Lazy loading можно дополнить предзагрузкой — загрузкой ленивых модулей в фоновом режиме после загрузки главной страницы.
Angular предоставляет стратегию PreloadAllModules:
@NgModule({
imports: \[
RouterModule.forRoot(routes, {
preloadingStrategy: PreloadAllModules
})
\],
exports: \[RouterModule\]
})
export class AppRoutingModule {}
Это уменьшает задержки при переходе по ленивым маршрутам, не увеличивая размер начальной загрузки.
Также можно реализовать кастомную стратегию предзагрузки, если требуется выборочная предзагрузка модулей на основе условий.
Lazy loading и Guard'ы
Если маршрут с ленивой загрузкой защищен canLoad, Angular не загрузит модуль, пока guard не вернёт true.
Пример:
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
canLoad: \[AuthGuard\]
}
-
canLoad блокирует загрузку JS чанка.
-
canActivate сработает после загрузки модуля.
Lazy loading и modules federation
В сложных enterprise-приложениях ленивую загрузку можно использовать для микрофронтендов через Module Federation (Webpack 5), позволяя загружать модули из других приложений во время выполнения.
Ошибки и подводные камни
Компонент не найден
Ошибка: NG04002: Can't bind to 'xyz' since it isn't a known property...
Причина:
- Компонент ленивого модуля не объявлен в его declarations или не экспортирован.
Модуль не загружается
Причина:
-
Указан RouterModule.forRoot() вместо forChild().
-
Ошибка в пути импорта.
-
Нарушена структура модулей (например, общий модуль импортирован не там).
Дублирование сервисов
Сервисы, определённые в ленивых модулях, создают новый инстанс, если они не providedIn: 'root'.
Проверка в сборке
Angular CLI при production-сборке (ng build --prod) создаёт отдельные чанки:
ng build --configuration production
В dist/ видно: admin-admin-module.js, user-user-module.js и т.д. Эти чанки загружаются отдельно при переходе к маршрутам.
Как проверить lazy loading в браузере
-
Откройте DevTools → Network → JS.
-
Перейдите на ленивый маршрут (/admin).
-
Должен появиться отдельный JS-файл (admin-admin-module.js), загруженный динамически.
Совмещение с standalone компонентами
С Angular 14+ появилась возможность использовать standalone-компоненты и также лениво их загружать:
{
path: 'about',
loadComponent: () =>
import('./about/about.component').then(m => m.AboutComponent)
}
Это упрощает структуру, убирая необходимость в модуле.
Lazy loading — важная часть Angular-архитектуры, особенно в больших приложениях. Он помогает сократить размер начального бандла, ускорить загрузку и масштабировать кодовую базу. Реализация требует правильной организации модулей, настройки маршрутов и понимания принципов загрузки.