Как оптимизировать производительность Angular-приложения?

Оптимизация производительности Angular-приложения включает множество аспектов: от работы Change Detection до ленивой загрузки модулей и оптимизации рендеринга DOM. Angular предоставляет богатый инструментарий и архитектурные подходы для эффективного управления производительностью как на уровне компонентов, так и приложения в целом.

1. Использование ChangeDetectionStrategy.OnPush

По умолчанию Angular использует стратегию Change Detection Default, при которой каждый компонент проверяется при любом изменении.

OnPush ограничивает проверки только компонентами, где:

  • изменились входные свойства (@Input()),

  • произошло событие внутри компонента,

  • вызван markForCheck() вручную.

@Component({
selector: 'app-item',
changeDetection: ChangeDetectionStrategy.OnPush,
...
})
export class ItemComponent {}

Это значительно снижает количество запусков Change Detection и уменьшает нагрузку на CPU.

2. Lazy Loading модулей

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

const routes: Routes = \[
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)
}
\];

Lazy-модули должны быть полностью изолированы и не содержать общие сервисы, чтобы избежать дублирования зависимостей.

3. Оптимизация шаблонов

  • Избегать сложных выражений в шаблонах: вычислять данные заранее в компоненте.

  • Не использовать function() или .map() в шаблоне (они пересоздаются на каждый рендер).

  • Минимизировать использование *ngIf и *ngFor в одной обёртке: при необходимости — выносить в отдельные компоненты.

  • Использовать trackBy в *ngFor для предотвращения перерендера всего списка.

<div \*ngFor="let item of items; trackBy: trackById">
{{ item.name }}
</div>
trackById(index: number, item: any) {
return item.id;
}

4. Разделение кода (Code Splitting)

Angular CLI по умолчанию использует Webpack и делит код на чанки. Это можно усилить:

  • Ленивая загрузка не только модулей, но и компонентов с loadComponent().

  • Динамический импорт изображений, переводов, heavy-библиотек и т.п.

loadComponent = () => import('./lazy.component').then(c => c.LazyComponent);

5. Асимметричная загрузка стилей и скриптов

В angular.json:

  • Основные стили — в "styles", подгружаются сразу.

  • Локальные или редко используемые — через динамическую загрузку:

loadStyle(url: string) {
const link = document.createElement('link');
link.href = url;
link.rel = 'stylesheet';
document.head.appendChild(link);
}

6. Использование Web Workers

Web Workers позволяют выполнять тяжёлые вычисления в фоновом потоке, не блокируя UI.

ng generate web-worker worker

Передача сообщений:

this.worker.postMessage(data);
this.worker.onmessage = ({ data }) => { ... };

Полезно для парсинга больших файлов, изображений, криптографии и т.п.

7. Снижение количества DOM-операций

  • Использовать Renderer2 вместо прямой работы с document.

  • Минимизировать манипуляции DOM во время Change Detection.

  • Использовать ng-container вместо лишних div, когда нужен только структурный шаблон.

8. Использование ngOptimizedImage

С Angular 15+ доступен NgOptimizedImage, который автоматически:

  • Генерирует srcset и sizes,

  • Предзагружает важные изображения,

  • Использует lazy-loading.

<img
ngSrc="image.jpg"
width="600"
height="400"
priority
alt="..." />

9. Использование standalone компонентов

С версии Angular 14 можно использовать компоненты без модулей. Это:

  • уменьшает избыточность в NgModules,

  • повышает tree-shaking,

  • ускоряет компиляцию и загрузку.

@Component({
standalone: true,
imports: \[CommonModule\],
templateUrl: './comp.html'
})
export class StandaloneComponent {}

10. Аудит и анализ сборки

Использовать команду:

ng build --prod --stats-json

Затем с помощью webpack-bundle-analyzer проанализировать:

npx webpack-bundle-analyzer dist/stats.json

Искать тяжёлые пакеты, неиспользуемые зависимости, повторяющиеся модули.

11. Caching & HTTP оптимизация

  • Использовать HttpClient с RxJS кешированием (shareReplay, tap, cacheMap).

  • Добавлять заголовки Cache-Control с бэкенда.

  • Кэшировать переводчики (i18n), конфиги и метаданные на клиенте.

12. SSR (Server-Side Rendering) + Prerendering

Использование Angular Universal позволяет отрисовывать HTML на сервере, что:

  • ускоряет Time-to-First-Byte,

  • улучшает SEO,

  • ускоряет начальную загрузку.

Можно использовать гибридную стратегию:

  • SSR для главных страниц,

  • lazy loading + клиентский рендеринг — для остальных.

13. Снижение зоновой активности (NgZone)

Если определённые процессы не требуют Change Detection:

  • использовать runOutsideAngular():
this.zone.runOutsideAngular(() => {
element.addEventListener('scroll', this.handler);
});
  • Или полностью отключить zone.js и управлять обновлением вручную через ChangeDetectorRef.

14. Использование detach() и detectChanges()

Если компонент статичен, можно временно отключить его от Change Detection:

constructor(private cdr: ChangeDetectorRef) {}
ngOnInit() {
this.cdr.detach();
}

Обновлять вручную:

this.cdr.detectChanges();

15. Использование Signals (Angular 17+)

Signals — новая реактивная модель, позволяющая:

  • избавиться от лишних @Input() и подписок,

  • избегать избыточных CD-проверок,

  • обновлять только необходимые компоненты.

readonly counter = signal(0);
increment() {
this.counter.update(v => v + 1);
}

Signals автоматически интегрированы с OnPush и zone-less режимом.

16. Оптимизация анимаций

  • Использовать AnimationBuilder или Web Animations API напрямую.

  • Удалять неиспользуемые состояния анимаций.

  • Использовать :enter, :leave осторожно, особенно в больших списках.

17. Разделение окружения (Dev vs Prod)

Сборка с флагом --configuration production включает:

  • AOT-компиляцию,

  • Minification + Tree Shaking,

  • Disable DebugInfo,

  • Optimize zone.js patches.

Оптимизация Angular-приложения требует комплексного подхода: от шаблонов и логики до сетевых запросов и архитектурных решений. Комбинация ленивой загрузки, Change Detection с OnPush, сигнальной модели и правильной сборки позволяет достигать высокой производительности даже в крупных приложениях.