Привет!
Это снова Сергей Кузнецов, руководитель отдела Frontend-разработки в AGIMA. Недавно я рассказывал здесь про оптимизацию сайта в разрезе показателей Google PageSpeed. И теперь хочу вернуться к этой теме. Сегодняшняя статья будет не слишком длинной, но, надеюсь, для многих полезной. Речь снова пойдет об оптимизации производительности. Я дам практические советы по тем или иным моментам.
Видео
В случае с одним видео на странице всё и так понятно, в прошлой статье были рекомендации на этот счет. А что делать, если видео на странице несколько? И не просто превьюшек, а видео, которые используются в виде анимированного фона,
Тут следует помнить, что, помимо использования современного формата webM, следует применять тот же подход, что и с изображениями — ленивую загрузку (Lazy loading).
Как мы помним, видео у нас должно автоматически демонстрироваться, поэтому такой простой способ, как добавление атрибута preload="none" (запрещает браузеру загружать не только само видео, но и любые видеоданные), нам не подойдет, так как он не будет работать в сочетании с Autoplay.
Как мы помним, видео у нас должно автоматически демонстрироваться, поэтому такой простой способ, как добавление атрибута preload="none" (запрещает браузеру загружать не только само видео, но и любые видеоданные), нам не подойдет, так как он не будет работать в сочетании с Autoplay.
Здесь нам поможет использование Intersection Observer API. Это инструмент, который позволяет в асинхронном режиме отслеживать пересечение нужных элементов (target, root) или с областью видимости (viewport). Другими словами, отслеживать событие, когда видео находится в зоне видимости пользователя. То же самое относится и к случаю, когда видео подгружается во фрейме.
В целом, можно как самостоятельно написать код, который будет отслеживать положение видео и включать проигрывание, только когда видео попадает в область видимости, так и использовать любой подходящий из готовых плагинов/библиотек Lazy loading. Правда, при использовании этого метода возникает небольшая побочная проблема: когда мы доскролливаем экран до видео, сперва наблюдаем только пустой экран, особенно если интернет не слишком быстрый. Но это можно решить выкладыванием первого кадра в качестве подложки или попыткой грузить видео чуть раньше, чем пользователь до него доскроллит. Но это менее надежно, так как пользователь может проматывать экран слишком быстро или, наоборот, остановиться перед самым видео.
Также хотелось бы напомнить, что и само видео можно оптимизировать с помощью конвертеров. Например, убрать звуковую дорожку там, где она не нужна, а еще уменьшить битрейт или разрешение.
SPA
Для SPA в целом подходят все те же рекомендации, что и для обычной верстки в плане ускорения работы/загрузки, но есть и нюансы.
С помощью сервиса Perf Track мы можем ознакомится с усредненной производительностью фреймворков
Например, вы можете настроить загрузку данных таким образом, чтобы нужные части загружались, когда они действительно нужны, используя те же принципы, что и для ленивой загрузки.
Все эти возможности заложены в современные фреймворки (такие как React или Vue) и позволяют разработчикам разбивать код приложения на несколько пакетов. Вы можете загружать их одновременно или по необходимости. Второй вариант может ускорить первое взаимодействие с пользователем. Вы можете, например, загрузить только те части, которые доступны пользователям немедленно, и отложить всё остальное.
Извлечение данных из памяти или веб-хранилища занимает намного меньше времени, чем отправка HTTP-запросов, даже с лучшими серверами. А память устройства намного быстрее, чем самая быстрая сеть. Так что кэширование — это отличный инструмент повышения производительности.
Конечно, не всё и не всегда можно закэшировать. Например, кэширование сторонних скриптов метрик/аналитики приведет к различным проблемам с корректностью обработки данных, но в целом это может здорово помочь.
Вы можете использовать Service Workers для кэширования статического содержимого в SPA. Это скрипты, работающие в фоновом режиме. Вы можете использовать их для уменьшения трафика и включения автономной работы. Когда браузер делает запрос на контент, он сначала проходит через сервис-воркер. Если запрошенный контент присутствует в кэше, то он будет извлечен и отобразится на экране. В других случаях он будет запрашивать ресурс из сети.
Для приложений с большим количеством интерактива можно использовать WebSocket.
Этот протокол обеспечивает двунаправленную связь между сервером и браузером пользователя. В отличие от HTTP, для получения новых сообщений клиенту не требуется постоянно отправлять запросы на сервер. Вместо этого браузер мониторит сервер и получает сообщение, когда оно будет готово.
В результате вы получаете взаимодействие существенно (иногда в разы) быстрее, чем через обычный HTTP.
Можно использовать такие сервисы, как socket.io, для реализации WebSockets для вашего SPA.
SSR
На сегодняшний день из-за повсеместного распространения SPА огромная часть обработки веб-страницы ложится на сторону браузера пользователя. И гораздо больше контента веб-сайта загружается и отображается с использованием файлов JavaScript.
Обработка такой большой функциональности на стороне браузера зачастую приводит к проблемам со скоростью загрузки страницы. Важные показатели ранжирования Google — First Contentful paint (FCP) и First Meaningful paint (FMP) — как правило, имеют низкий рейтинг в современных JS-фреймворках. Браузер вынужден ждать загрузки всех ресурсов, затем файлы JavaScript обрабатываются для визуализации приложения. Это более сложный и медленный процесс, чем рендеринг на стороне сервера.
Рендеринг на стороне сервера (SSR) — это попытка объединить преимущества современных фреймворков JS с некоторыми преимуществами рендеринга на стороне сервера, такими как более быстрое время загрузки и оптимизация для поисковых систем (SEO) через легко индексируемый контент. Этот подход позволяет с помощью Node.js запускать JavaScript-код на сервере и готовый результат отправлять пользователю, избегая лишней нагрузки на его браузер и компьютер.
В результате мы можем быть уверены, что обработка нашего приложения и рендеринг страницы на стороне сервера происходит существенно быстрее, чем на каком-нибудь маломощном устройстве пользователя. Для этого были созданы и успешно применяются такие фреймворки, как Vue Nuxt и React Next.
Но даже при таком подходе находятся кое-какие проблемы с производительностью. Например, ситуация, связанная с так называемой гидратацией (hydratation). Что это вообще такое? Если кратко, то это процесс на стороне пользователя, когда статический html, отправленный сервером, превращается силами приложения в динамический DOM, который позволяет реагировать на изменения данных и взаимодействия на стороне пользователя.
Тут, кстати, следует отметить, что при использовании серверного рендеринга с гидратацией некоторая часть html может быть подвержена изменениям, которые произведет браузер, достраивая ваш html до семантически верного. Следите за этим.
Так вот, процесс гидратации имеет определенный недостаток — это время до взаимодействия (Time to Interactive (TTI)). Даже если всё организовано идеально на стороне сервера и страница имеет быструю FCP, пользователь сможет с ней взаимодействовать только после регидратации, которая иногда выполняется довольно медленно. Это большой минус в части UX.
Еще один показатель — задержка первого взаимодействия (First Input Delay (FID)) — одна из метрик производительности веб-страниц, которая, как понятно из названия, показывает время, когда пользователь впервые начал взаимодействовать с веб-страницей, и до момента, когда веб-браузер может обработать данное взаимодействие.
Время регидратации напрямую влияет на этот показатель. И чем больше компонентов и логики на вашей странице, тем стремительнее увеличивается этот показатель. И мы опять приходим к ленивой загрузке, на этот раз для hydration.
По сути, этот метод позволяет нам не гидратировать определенные компоненты на стороне пользователя или гидратировать их только в определенных условиях.
Качество сети
Напоследок хотелось бы упомянуть о таком интересном инструменте, как Network Information API. Эта технология позволяет адаптировать ресурсы, которые вы предоставляете пользователям, в зависимости от качества их соединения.
Иначе говоря, с помощью Network Information API мы позволяем веб-приложению получать доступ к информации о сети пользователя и, исходя из ее параметров, отдавать тот или иной контент или тот же самый, но в разном качестве.
Конечно, это не влияет на максимальную производительность, но позволяет гибко регулировать нагрузку в зависимости от качества соединения. Например, мы можем управлять качеством видео, отложить загрузку тех или иных второстепенных данных, а то и вовсе предложить пользователю переключиться в автономный режим. К сожалению, на текущий момент поддержка этой технологии еще не полная, но она наверняка будет развиваться в будущем.
Комментарии и обсуждения статьи в нашем блоге на Хабр.