Один из наших проектов – сайт крупной компании по продаже автозапчастей и комплектующих. Это интернет-магазин, аудитория которого насчитывает около 500 тысяч уникальных пользователей в месяц. Оптимизировать нужно было главную страницу этого интернет-магазина.
На сайте, на которым велась работа, используется APM сервис elastic – система сбора метрик, согласно которым можно отследить производительность ресурса. Так как сайт долгое время не оптимизировался, а только дополнялся новыми фичами, показатели стали падать.
Не все показатели пока удалось вытянуть в зеленую зону, но улучшение для многих из них получилось кратным. Пока дальнейшие работы планируются, расскажем, какие шаги предприняли для текущего результата.
TS-Prune
Для начала используем сервис, который позволяет быстро найти мертвые экспорты или код в проекте.
Пример команды для package.json:
"deadcode-check": "npx --yes ts-"deadcode-check": "npx --yes ts-prune -s \"pages/[**/]?
(_app|_document|_error|index)|store/(index|sagas)|styles/global\""
Такой результат получаем:
После отработки в консоле будет список проблемных мест. По ним проходимся вручную и удаляем мертвые сегменты.
Depcheck
Есть и другой пакет для оптимизации. С его помощью получаем список неиспользуемых npm пакетов. Затем вручную проходимся по списку и удаляем все лишнее, тем самым уменьшая вес проекта и наводя порядок.
Depcheck нужно использовать несколько раз, потому как после каждого прогона и удаления ненужных библиотек могут появится новые, которые становятся рудиментарными после первого прогона. Например, удаленная в первой очереди библиотека использовала другую, которая уже на втором прогоне определится как мертвая зависимость.
Поиск дублирующих npm пакетов
Используем плагин, который анализирует проект и показывает список дублей с разными версиями.
Вот пример результата его работы на примере проекта сортировки:
Однако от видов выдачи результатов может зависеть алгоритм дальнейших действий.
Например, вот такая выдача:
Здесь явно понимаем, что нужно обновить пакеты. Обновив зависимости, можно будет уменьшить вес бандла.
Другой пример:
В этом случае также требуются обновления. Если не понятно, что и где обновлять, в настройках вебпака добавляем alias, который подскажет сборщику, какую именно версию обновить и откуда ее взять. На примере redux:
Оптимизация картинок
Картинки на сайте до оптимизации не масштабировались в зависимости от размеров экрана. Мы поменяли все картинки на next image. Затем поменяли приоритеты загрузки. Те, что первыми попадают во viewport, должны загружаться с высоким приоритетом и без lazyloading – то есть максимально быстро. Это влияет на скорость отрисовки сайта.
- Используем для всех картинок модуль next/image.
- Добавляем современные форматы изображений вместо jpg и png. Получаем лучшее сжатие без потери качества и более быструю загрузку.
images: {
formats: ['image/avif', 'image/webp'],
}
- Приоритезируем загрузку картинок above the fold (тех, что находятся в viewport при первоначальной загрузке страницы) - свойство Priority.
- Для векторных картинок выставляем свойство unoptimized.
Code splitting
Выяснилось, что, в коде на каждой странице много дубликатов. Элементы, которые должны один раз загрузиться, закэшироваться в браузере и использоваться всеми страницами, были вставлены в код каждой из страниц и потребляли ресурсы при загрузке снова и снова.
Дубликаты элементов в коде появились из-за отмены дефолтного разбиения кода на чанки в next.js. Прежние разработчики применили такое решение, чтобы работала Linaria, которая призвана увеличивать производительность. Однако из-за отключения разбиения производительность наоборот падала.
В итоге мы убрали строку, которая блокирует разбиение чанков, и это дало практически 50% прирост в производительности.
Встроенный механизм next разбивки js кода на чанки протестирован и рекомендован для production. Отключать его не нужно, иначе код начинает дублироваться для каждой страницы, хотя переиспользуемые модули (react, react-dom, ui kit и т.д.) должны выноситься в отдельные (общие) чанки, а не грузиться заново на каждой странице.
Убираем блокирующие ресурсы
Далее использовали webpagetest, на котором можно запускать тесты на производительность сайтов и получать разные метрики. После теста выяснилось, что в коде сайта есть блокирующие скрипты.
Блокирующий скрипт стали загружать асинхронно и только на тех страницах, где это было необходимо. Синхронная загрузка блокирует очередь загрузки. Все скрипты должны быть не блокирующими.
Пример с aplaut:
Компактные сборки
Вместо Terser используем swcMinify для сжатия js (на крупном проекте экономия порядка 200Kb).
Собираем js только для современных браузеров. Список браузеров по умолчанию в Next, можно переопределить в своем browserslist.
Сторонние скрипты
Чтобы сократить общее время блокировки (TBT), сторонние скрипты для сайта (например, Google analytics и Yandex metrika) должны быть подключены с использованием next/script и соответствующих стратегий загрузки (чаще всего afterInteractive).
Вот пример подключения Google Analytics c использованием next/script.
Скрытые компоненты
На сайте есть компоненты, которые скрыты до того момента, пока пользователь не начнет с ними взаимодействовать. Например, это компоненты в модальных окнах и сайдбарах. Для них используем Dynamic Imports, чтобы уменьшить объем js, необходимый для первоначальной загрузки страницы. Загрузка js скрытого компонента откладывается до момента пользовательской потребности – взаимодействия с компонентом.
Ускорение билда
Можно отключить проверку eslint в next.config.js. Если линт отрабатывает на этапе комита, делать линт при билде нет смысла. Этот нюанс не влияет на пользователя, но увеличивает скорость сборки.
Также можно использовать параметр --no-lint в package.json:
Что еще полезно
Если работа на проекте ведется регулярно, сопровождаясь частыми релизами, стоит подумать о настройке мониторингов производительности после каждого релиза. Так, после внедрения новых фич, можно будет сразу отрабатывать проседания показателей. Это гораздо удобнее, чем дожидаться критического падения производительности и оптимизировать все целиком.
А чтобы в целом держать руку на пульсе, советуем чек-лист по улучшению производительности страниц от Виталия Фридмана. Чек-лист меняется каждый год, поэтому советы и подходы в нем получаются максимально актуальными и пригодными для использования в проектах.