12 советов по оптимизации веб-приложения

Артем Потемин Артем Потемин Технический директор 02 Декабрь 2024 6 мин чтения
12 советов по оптимизации веб-приложения

Любое веб-приложение должно работать бесперебойно, а главное, быстро. Это влияет на пользовательский опыт, повышает конверсию… а если перевести с маркетингового на человеческий — делает пользователей счастливее.

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

HTML

1. Изображения уменьшенного размера

Без картинок не обходится ни один продукт. При этом одна картинка высокого разрешения может весить столько же, сколько весь сборка приложения. А если таких картинок много?

Каталог товаров с несколькими рядами фотографий обуви
Обилие неоптимизированных картинок может заметно утяжелять и замедлять страницу

Чтобы уменьшить вес медиа, используйте файлы необходимых размеров — не больше. Если требуется картинка 200*300px — нет никакой нужды использовать разрешение 4k.

2. Ленивая загрузка картинок вне пределов окна браузера

Взгляните ещё раз на картинку в первом пункте👆 Пользователь не видит изображения за границей экрана. Не стоит тратить ресурсы на загрузку всех изображений сразу. Лучшая практика — загружать картинки по мере необходимости, когда пользователь доскроллит до них. Это называется «ленивая загрузка».

Реализовать это решение помогают специальные плагины, например, для jQuery или для Vue.

3. Async и defer при подключении элемента script

Раньше рекомендовали подключать js-файлы в конец страницы, чтобы не мешать её отрисовке, ведь выполнение кода блокирует поток. В современных браузерах атрибут async выполняет скрипты сразу после загрузки, а defer — после завершения разбора HTML.

Следовательно, если порядок выполнения скриптов важен, то лучше использовать атрибут defer, так как он гарантирует, что скрипты будут выполняться в том же порядке, в котором появляются на странице. Если же порядок выполнения скриптов не имеет значения, используйте атрибут async — он может ускорить загрузку страницы за счёт параллельной загрузки и выполнения скриптов.

4. Cache-Control

HTTP-заголовок ответа Cache-Control управляет кешированием. Его задают на сервере. Пример:

Cache-Control: max-age=3600, must-revalidate

Вот возможные директивы заголовка с расшифровкой:

  • public — документ кешируется во всех доступных кешах
  • private — кешируется браузером, не кешируется прокси-сервером
  • no-cache — ответ можно хранить в кеше, но перед повторным использованием необходимо успешно проверить его актуальность на сервере
  • no-store — кеш не должен сохранять ответ для повторного использования
  • max-age=time, must-revalidate — указывает браузеру, сколько секунд хранить документ в кеше
  • max-age=time, proxy-revalidate — указывает прокси-серверу, сколько секунд хранить документ в кеше

Выбор конкретного значения зависит от того, как будет вести себя контент на странице. Если это страница со статичным контентом, который не должен меняться — можно установить значение public. Если это страница, данные на которой меняются в реальном времени — разумно установить no-cache. Чаще всего используется кэш max-age=3600, must-revalidate.

5. Lighthouse

На самом деле рекомендации по полноценной HTML-оптимизации могут потянуть на небольшую книгу. Или пять. Чтобы не держать всё в голове, можно использовать прекрасный инструмент от Google — Lighthouse. Это расширение для браузеров на основе Chromium, но в последних версиях Chrome он существует прямо в инструментах разработчика:

Панель создания отчёта Lighthouse в инструментах разработчика
Создание отчёта Lighthouse в инструментах разработчика

Проверяйте отчёты Lighthouse в процессе и по окончанию разработки. Этот инструмент оценивает производительность страницы, указывает на проблемы и вообще позволяет избежать большинства распространённых ошибок.

JS

6. Тяжёлые операции в браузере — не лучший выбор

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

7. ...не используйте цепочки методов массива без необходимости

В коде нередко встречаются такие конструкции:

let myArray = []

const newMyArray = myArray.map(value => mapFunctionForMyArray(value))

.filter(value => filterFunctionForMyArray(value))

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

Впрочем, это не всегда актуально. С использованием, например, одного forEach код станет менее очевидным и понятным. Если myArray состоит из десятка элементов, этот совет можно проигнорировать, но если данных много — то преимущество будет в скорости обработки операции большого массива данных.

Отдельно стоит сказать про оператор расширения. Его часто используют для копирования массивов или объектов. Но не все знают, что const newArray = [...oldArray] — это на самом деле тоже цикл по newArray. Не используйте его бездумно, особенно во вложенном формате.

8. WebWorker для выполнения ресурсоёмких вычислений

Случается, что нужно сделать операцию над большим количеством данных. Ещё сложнее, когда это нужно сделать в реальном времени, пока пользователь взаимодействует с системой. И если пункт 6 этих рекомендаций реализовать не удалось, можно попробовать использовать webWorker.

Для Vue 3 я использую функцию useWebWorkerFn из VueUse. Он позволяет легко выполнять задачу в отдельном потоке. В него можно передавать любые примитивы JS, но есть нюанс — у него другой контекст и в него нельзя передавать:

  • DOM-элементы.
  • Функции и сложные объекты, например, классы с методами.
  • DOM-API.
  • Window, document, и другие свойства глобального объекта браузера.

9. Wasm для очень тяжёлых задач

Если у вас есть сложная и ресурсоёмкая задача, пункт 6 не сработал, пункт 8 помог, но всё ещё медленно — можно попробовать WebAssembly (wasm). Например, в задачах, связанных с большими данными, это позволяет получить значительный прирост производительности, хоть и не всегда. В wasm компилируется код на многих языках — вот, например, руководство по Go.

10. Виртуальная прокрутка для объектов с большим количеством элементов

Если у вас на странице есть контейнер со списком внутри, длинная таблица или что-то похожее — используйте виртуальную прокрутку. Он даёт ощутимый прирост в производительности. Я, например, использую virtual-scroll.

11. Вкладка Performance в инструментах разработчика

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

12. При использовании фреймворков не делайте реактивными данные, которые в этом не нуждаются

При работе с Vue, React и другими фреймворками дважды подумайте над тем, нужна ли вам реактивность конкретной переменной. А если это большой массив — подумайте трижды. Глубокая реактивность запускается при изменении любого элемента, в том числе и дочернего. Это может вызвать проблемы с производительностью из-за большого количества зависимостей, а также привести к проблемам с синхронизацией. В таком случае стоит использовать, например, computed-свойства в Vue. Это помогает минимизировать избыточные обновления.

Более того, если переменная является объектом/массивом, и она не будет изменяться в будущем — лучше обернуть её в Object.freeze, это даст преимущество — замороженные объекты оптимизируются движками JavaScript, так как их структура и значения остаются неизменными. Это позволяет экономить ресурсы на внутренних проверках.

Обсудить проект

Расскажите о задаче вашей компании.