Ускорение начальной загрузки приложения
-
Следите за зависимостями. Уже писал об этом ранее, но хочу рассказать про другой кейс, что ещё интересного можно увидеть в сборке (не «удалите moment.js», о чём пишут в 99 % статей на тему анализа зависимостей). В этот раз воспользуемся инструментом
source-map-explorer
.Меня сильно смутили эти
es5
в части, занимающей треть всего бандла, учитывая"target": "ES2022"
вtsconfig.json
. Более того, вnode_modules
на каждый пакет есть две папки:es5
иes6
. Написал разработчикам, признались, что это из-за ихpackage.json
, пообещав однажды его изменить. Но можно это исправить сразу, не дожидаясь обновления.Итак, нам нужно немного поменять процесс сборки, для этого в
devDependencies
добавляем пакет@angular-builders/custom-webpack
. Далее правимangular.json
:"architect": { "build": {
"builder": "@angular-devkit/build-angular:browser","builder": "@angular-builders/custom-webpack:browser", "options": { "customWebpackConfig": { "path": "./extra-webpack.config.js", "replaceDuplicatePlugins": true }, ... "serve": {"builder": "@angular-devkit/build-angular:dev-server","builder": "@angular-builders/custom-webpack:dev-server",И создаём конфиг вебпака, в котором прописываем алиасы для всех пакетов:
const path = require('path'); module.exports = { resolve: { alias: { '@ag-grid-community/client-side-row-model': path.resolve('./node_modules/@ag-grid-community/client-side-row-model/dist/esm/es6/main'), '@ag-grid-community/core': path.resolve('./node_modules/@ag-grid-community/core/dist/esm/es6/main'), ... 'ag-charts-community': path.resolve('./node_modules/ag-charts-community/dist/esm/es6/main'), } } }
Пересобираем всё это дело.
В результате сэкономили 390 KB в прод-сборке.
-
Следите за нетворками.
Вот этот огромный запрос на тысячи записей и сотни килобайт для селектов на старте приложения кажется избыточным, поскольку нам они не нужны все сразу. Сюда же хочется добавить и подпункт «следите за алгоритмами», поскольку изначально в коде была следующая логика: полученный массив сохранялся в стор, и из него готовились десятки списков для каждого из селектов путём банального
array.filter()
. Т. е. по одному и тому же массиву пробегались десятки раз. Благо это тысячи, а не миллионы записей, не столь критично, но ресурсы всё равно расходуются.Меняем логику, чтобы запрашивались лишь необходимые данные, самих запросов будет больше, но работать это будет быстрее. И, конечно же, дабы не ходить повторно в сеть, сохраняем полученные данные, но не в стор, а в собственный сервис, используя
Observable
сshareReplay(1)
.(На главной странице нет ни одного селекта, поэтому данный запрос вообще исчез из нетворков, значительно ускорив загрузку приложения.)
-
Используйте ленивые модули и предзагрузку. Допустим, у нас есть приложение на 10 мегабайт, в котором 10 разделов. И довольно долго целиком загружать и парсить такой бандл. Используем
lazy-loading
, получаем 10 модулей, и теперь для первоначальной загрузки нам надо загрузить 1 мегабайт.Но теперь при переходе в другие разделы нам приходится ждать загрузки соответствующего модуля. Чтобы такого не было, мы можем предзагружать эти модули заранее. «Но в чём смысл этих двух манипуляций, мы одинаково загружаем те же самые 10 мегабайт на старте?», — спросил меня коллега.
А смысл в том, что сначала мы загружаем модуль текущего раздела, следовательно, парсить код начинаем раньше, и в целом этого кода для парсинга гораздо меньше. А другие модули загружаются уже после того, как мы загрузили всё необходимое для инициализации приложения, в фоне. Причём это поведение можно настроить, например, если у пользователя в настройках стоит экономия трафика, или просто плохой интернет, то мы не будем загружать то, что не требуется в данный момент.
Синяя линия — это DOMContentLoaded, красная — Load, а дальше уже тот самый
preloading
модулей, которые могут понадобиться при работе с приложением.
{{ 'Comments (%count%)' | trans {count:count} }}
{{ 'Comments are closed.' | trans }}