Производительность в Angular
Измерение производительности
Когда речь заходит о производительности Single Page Applications, мы можем говорить о сетевой производительности и о RunTime Performance.
Инструменты для измерения RunTime производительности:
- Benchmark.js — библиотека для замеров производительности и сбора статистики.
- Chrome Dev Tools
- @angular/benchpress — фреймворк для тестов производительности
Способы увеличения производительности
Разделение кода с помощью Angular
Есть два основных подхода к разделению кода:
- Разделение кода на уровне компонентов
- Разделение кода на уровне маршрута
Основное различие между этими двумя методами состоит в том, что с разделением кода на уровне компонентов мы можем лениво загружать отдельные компоненты даже без навигации по маршруту. Например, мы можем загрузить компонент, связанный с чатом, только после того, как пользователь кликнет на поле ввода.
Благодаря разделению кода на уровне маршрута мы лениво загружаем отдельные маршруты. Например, если пользователь находится на домашней странице приложения и переходит на страницу галереи, Angular сначала загрузит соответствующий пакет, а затем отобразит маршрут.
Для разделения кода на уровне компонентов используют пакеты:
- ngx-loadable
- @herodevs/hero-loader
Для разделения модулей на уровне маршрутов используют lazyLoading стратегию загрузки модулей. Чтобы создать lazyLoading маршрут вручную, нам необходимо:
- Создайте новый модуль
- С помощью loadChildren объявите ленивый маршрут в родительском модуле
- Создайте новый компонент в ленивом загружаемом модуле
- Добавьте объявление активного маршрута в ленивом модуле
const routes: Routes = [
{
path: 'items',
loadChildren: () => import('./items/items.module').then(m => m.ItemsModule)
}
];
В модуле маршрутизации ленивого модуля добавьте маршрут для компонента.
const routes: Routes = [
{
path: '',
component: ItemsComponent
}
];
Preloading модулей
Есть встроенная стратегия, которая предварительно загружает все модули в приложении. Вы можете использовать его, настроив роутер Angular:
import { PreloadAllModules } from '@angular/router';
// ...
RouteModule.forRoot(ROUTES, { preloadingStrategy: PreloadAllModules })
// ...
Риск этой стратегии заключается в том, что в приложении с большим количеством модулей это может увеличить потребление сети, а также заблокировать основной поток, когда Angular регистрирует маршруты предварительно загруженных модулей.
Для более крупных приложений мы можем применить более продвинутую стратегию предварительной загрузки:
- Quicklink — предварительно загружать только модули, связанные с видимыми ссылками в области просмотра
- Predictive prefetching — предварительно загружайте только те модули, которые могут понадобиться в следующий раз
Реализовать Quicklink вам поможет пакет ngx-quicklink
.
Предварительный рендеринг приложения Angular
Предварительная отрисовка (Prerendering) — это запуск клиентского приложения во время сборки для записи его начального состояния в виде статического HTML.
При использовании техники предварительной загрузки страниц все маршруты приложения компилируются во время сборки и сохраняются как статические HTML-страницы в файловой системе. Эти HTML-страницы могут быть предоставлены клиенту с помощью CDN, таким образом всякий раз, когда мы переходим на какой-либо маршрут, будем получать предварительно обработанные HTML-страницы. После приложение переключается с предварительно отрисованного на полностью интерактивное.
В случае рендеринга на стороне сервера (Server Side Rendering), когда пользователь переходит по URL-адресу, сервер компилирует приложение (визуализирует на сервере) и отправляет сгенерированную HTML-страницу обратно клиенту.
Преимущество предварительного рендеринга заключается в том, что он может обрабатывать запросы очень быстро, потому что ничего не нужно генерировать на лету.
Angular Universal — это набор инструментов, который позволяет нам выполнять рендеринг на стороне сервера (SSR) и предварительный рендеринг для наших приложений Angular.
Для установки Angular Universal используйте команду:
ng add @nguniversal/express-engine
Для регистрации FCP (First Contentful Paint — первая загрузка страницы) в браузере мы добавляем скрипт в index.html.
<script>
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntriesByName('first-contentful-paint')) {
console.log('FCP: ', entry.startTime);
po.disconnect();
}
});
po.observe({type: 'paint', buffered: true});
</script>
Настройка для предварительной загрузки находится в angular.json
и представляет собой список маршрутов, которые вы хотите предварительно обработать.
"prerender": {
"builder": "@nguniversal/builders:prerender",
"options": {
"browserTarget": "universal-app:build:production",
"serverTarget": "universal-app:server:production",
"routes": [
"/",
"/about-us",
"/contact-us"
]
},
"configurations": {
"production": {}
}
}
Для автоматического добавления роутеров для предварительной загрузки можно заменить параметр routes
на routesFile
, который позволит указать относительный URL для загрузки маршрутов.
"prerender": {
"builder": "@nguniversal/builders:prerender",
"options": {
"browserTarget": "universal-app:build:production",
"serverTarget": "universal-app:server:production",
"routesFile": "./pre-render-routes.txt"
},
"configurations": {
"production": {}
}
}
Файл pre-render-routes.txt
будет выглядеть следующим образом:
/
/about-us
/contact-us
После запускаем команду:
npm run prerender
Изменение Change Detection Strategy
По умолчанию цикл обнаружений изменений в приложении Angular запускается практически при каждом взаимодействии с пользователем, но при настройке ChangeDetectionStrategy
в OnPush
— компонент будет проверяться только при изменении входных параметров @Inputs()
, ивентах внутри самого компонента или же при ручном запуске обнаружения изменений.
@Component({
selector: 'app-list',
templateUrl: './list.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
Более детально описано в статье Angular 2+: Стратегия обнаружения изменений.
Источники: blog.angular.io, ngdevelop.tech, slideshare.net, medium.com