Как стать автором
Обновить

Как в 1.5 раза повысить производительность фронтенда высоконагруженного интернет-магазина на Next.js

Уровень сложностиСредний
Время на прочтение5 мин
Количество просмотров10K

Приветствую! Меня зовут Андрей Степанов, я CTO во fuse8. Мне интересно знакомиться с опытом коллег по цеху и делиться своим. В сфере я уже больше 20 лет. В этой статье – небольшое погружение в задачу по повышению производительности крупного сайта, много полезных ссылок и инструментов, которые вы сможете использовать для своих проектов.

Один из наших проектов – сайт крупной компании по продаже автозапчастей и комплектующих. Это интернет-магазин, аудитория которого насчитывает около 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 – то есть максимально быстро. Это влияет на скорость отрисовки сайта. 

  1. Используем для всех картинок модуль next/image.

  2. Добавляем современные форматы изображений вместо jpg и png. Получаем лучшее сжатие без потери качества и более быструю загрузку.

images: { 
formats: ['image/avif', 'image/webp'], 
}
  1. Приоритезируем загрузку картинок above the fold (тех, что находятся в viewport при первоначальной загрузке страницы) - свойство Priority.

  2.  Для векторных картинок выставляем свойство unoptimized.

<Image
	scr="/static/icons/mainPage/qualityControl.svg"
	width={40}
	height={40}
	unoptimized
	priority
	alt="Quality control"
     />

Code splitting

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

Отчет webpack-bundle-analyzer. Красным выделены повторяющиеся модули.
Отчет webpack-bundle-analyzer. Красным выделены повторяющиеся модули.

Дубликаты элементов в коде появились из-за отмены дефолтного разбиения кода на чанки в next.js. Прежние разработчики применили такое решение, чтобы работала Linaria, которая призвана увеличивать производительность. Однако из-за отключения разбиения производительность наоборот падала. 

В итоге мы убрали строку, которая блокирует разбиение чанков, и это дало практически 50% прирост в производительности. 

Встроенный механизм next разбивки js кода на чанки протестирован и рекомендован для production. Отключать его не нужно, иначе код начинает дублироваться для каждой страницы, хотя переиспользуемые модули (react, react-dom, ui kit и т.д.) должны выноситься в отдельные (общие) чанки, а не грузиться заново на каждой странице. 

Так делать нельзя:

// next.config.js 
webpack: (config, { isServer }) => { 
config.optimization.splitChunks = false; // 
return config;
}

Убираем блокирующие ресурсы

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

Блокирующий скрипт стали загружать асинхронно и только на тех страницах, где это было необходимо. Синхронная загрузка блокирует очередь загрузки. Все скрипты должны быть не блокирующими.

Пример с aplaut:

Компактные сборки

Вместо Terser используем swcMinify для сжатия js (на крупном проекте экономия порядка 200Kb).

// next.config.js 
module.exports = { 
swcMinify: true, 
}

Собираем js только для современных браузеров. Список браузеров по умолчанию в Next, можно переопределить в своем browserslist.

"chrome 61", 
"edge 16", 
"firefox 60", 
"opera 48", 
"safari 11"
// next.config.js 

module.exports = { 
experimental: { 
legacyBrowsers: false, 
}, 
} 

Сторонние скрипты

Чтобы сократить общее время блокировки (TBT), сторонние скрипты для сайта (например, Google analytics и Yandex metrika) должны быть подключены с использованием next/script и соответствующих стратегий загрузки (чаще всего afterInteractive).

Вот пример подключения  Google Analytics c использованием next/script.

Скрытые компоненты

На сайте есть компоненты, которые скрыты до того момента, пока пользователь не начнет с ними взаимодействовать. Например, это компоненты в модальных окнах и сайдбарах. Для них используем Dynamic Imports, чтобы уменьшить объем js, необходимый для первоначальной загрузки страницы. Загрузка js скрытого компонента откладывается до момента пользовательской потребности – взаимодействия с компонентом. 

Ускорение билда

Можно отключить проверку eslint в next.config.js. Если линт отрабатывает на этапе комита, делать линт при билде нет смысла. Этот нюанс не влияет на пользователя, но увеличивает скорость сборки.

module.exports = { 
eslint: { 
// Warning: This allows production builds to successfully complete even if // your project has ESLint errors. 
ignoreDuringBuilds: true, 
}, 
}

Также можно использовать параметр --no-lint в package.json:

"scripts": { 
"dev": "ts-node server.ts", 
"build": "next build --no-lint", 
"build-analyze": "rimraf .next && cross-env ANALYZE=true next build", 
"start": "cross-env ts-node server.ts", 
"lint": "tsc && eslint **/*.{js,jsx,ts,tsx} --fix" 
}

Что еще полезно

Если работа на проекте ведется регулярно, сопровождаясь частыми релизами, стоит подумать о настройке мониторингов производительности после каждого релиза. Так, после внедрения новых фич, можно будет сразу отрабатывать проседания показателей. Это гораздо удобнее, чем дожидаться критического падения производительности и оптимизировать все целиком.

А чтобы в целом держать руку на пульсе, советуем чек-лист по улучшению производительности страниц от Виталия Фридмана. Чек-лист меняется каждый год, поэтому советы и подходы в нем получаются максимально актуальными и пригодными для использования в проектах.

За подготовку и содействие в составлении материала выражаю большую благодарность фронтенд-разработчику Сергею Пестову.

Теги:
Хабы:
Всего голосов 11: ↑10 и ↓1+11
Комментарии18

Публикации

Информация

Сайт
fuse8.ru
Дата регистрации
Дата основания
2001
Численность
51–100 человек

Истории