JavaScript Performance: советы и рекомендации

Kate

Administrator
Команда форума
Каждый опытный разработчик знает, насколько важно поддерживать производительность приложения и что это за боль. Когда дело доходит до загрузки, разница между успешным бизнесом и катастрофой составляет всего несколько секунд. Таким образом, разработчик отвечает за то, чтобы приложение обеспечивало лучший пользовательский интерфейс, более высокую конверсию и, в конечном счете, более довольных заказчиков. В этой статье Александр Габдрафиков — Senior Software Engineer в EPAM Anywhere — делится советами и приемами по улучшению производительности JavaScript, основанными на многолетнем опыте.

>16 миллисекунд​

JavaScript выполняет задачи, используя цикл событий. Идея проста: существует бесконечный цикл, который ожидает выполнения задания, выполняет его и возвращается в состояние ожидания, пока не поступит новое задание.

Работа, которую выполняет цикл событий, состоит из дискретных задач (например запуск загруженного скрипта или перемещения мыши), микрозадач и рендеринга изменений, которые мы кратко обсудим ниже. Рендеринг не происходит, пока выполняется какая-либо другая задача. Поэтому для полноценного взаимодействия с пользователем жизненно важно, чтобы все в цикле событий шло своевременно.

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


Большинство задач по JavaScript достаточно просты, чтобы их можно было выполнить за такое короткое время. Но современные веб-приложения с каждым днем становятся все сложнее, превращая клиентскую часть в феерию с изобилием функций и вычислений, намного превышающих наш порог в 16 миллисекунд.

Обработка больших массивов данных​

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

Решение​

Разбейте вычисления на более мелкие части с помощью setTimeout.

Второй вариант — используйте веб-воркеры. Они запускают скрипты в фоновом режиме, чтобы те не блокировали задачи в основном потоке, давая браузеру возможность показать изображение как можно скорее.

Чрезмерное использование сторонних библиотек​

Оптимизация далеко не универсальна среди сторонних библиотек, даже самых популярных. Возьмем, например, bcrypt, который хеширует строку с 13 раундами хеширования. Каждый раунд занимает около двух секунд, блокируя основной поток на довольно долгое время и останавливая выполнение других подключений.

Хотя это не проблема с 16 миллисекундами, поскольку это внутренний процесс, который не влияет напрямую на рендеринг, шифрование — отличный пример того, как неоптимизированные библиотеки могут нанести ущерб вашему приложению.

Решение​

Лучшее решение здесь — выбрать оптимизированные библиотеки. Попробуйте найти библиотеки, специально разработанные для Node.js, поскольку они используют привязки C++, которые позволяют распараллеливать потоки и выполнять вычисления до трех раз быстрее.

Layout​

Типичная проблема производительности, особенно для SPA-приложений, которые на лету создают и уничтожают контент. Layout — это шаг в очереди рендеринга, когда ваш браузер определяет, где должен отображаться каждый элемент страницы, оценивает его размер и отношение к другим объектам.

Неудивительно, что чем больше объектов DOM находится на странице, тем больше времени занимает процесс. Однако самая сложная часть заключается в том, что даже наименее значительное изменение стиля делает недействительными предыдущие вычисления и запускает совершенно новый шаг Layout.

Решение​

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

Большие сборки​

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

Таким образом, оптимизация файлов JavaScript — важная часть повышения производительности твоего приложения. Используйте Webpack Bundle Analyzer, чтобы увидеть размер выходных файлов и то, из чего они состоят.

Решение​

Решение 1. Для React лучшим решением будет применение отложенной загрузки. React.lazy позволит вам использовать динамический импорт, который знает, как разделить код на части, вместо того, чтобы включать весь код в один файл целиком.

Решение 2. Если уменьшить размер файлов невозможно, попробуйте кэшировать их, чтобы они не перезагружались каждый раз, когда они нужны приложению. Для кэширования файлов есть 4 заголовка:

  • ETag — идентификатор, позволяющий веб-серверу избежать повторной отправки полного ответа, если содержимое не изменилось;
  • Cache-Control — содержит инструкции, которые вы можете использовать для управления своим кэшем;
  • Expires — показывает время жизни кэша;
  • Last-Modified — содержит дату и время последнего изменения файла.
Решение 3. Сжать файл. Хотя большинство браузеров поддерживает форматы сжатия Gzip и Brotli, я советую использовать последний, поскольку он более действенный.

 
Сверху