Привет читателям Хабра!
Меня зовут Сергей Кузнецов, я руковожу отделом frontend-разработки в компании AGIMA. Сегодня мне бы хотелось поговорить про оптимизацию сайта в разрезе показателей Google PageSpeed.
Статей разной свежести и полезности много, но обычно в них даются наиболее простые и распространенные рекомендации, которые известны любому, кто хоть немного дружит с вебом. Что-то вроде: «Выносите скрипты вниз страницы» или «Минифицируйте файлы». Но что делать, когда базовые рекомендации были учтены еще на этапе разработки, а показатели все еще далеки от идеальных?
Давайте попробуем разобраться с этой проблемой. Поговорим о тех пунктах, которые описаны в рекомендациях PageSpeed Insights довольно обобщенно, например: «Минимизируйте работу в основном потоке» или «Сократите время выполнения кода». Отличные рекомендации, но только что конкретно требуется?
Ниже примеры вопросов, которые могут поставить в тупик недостаточно погруженных в тему оптимизации разработчиков.
Что делать с аналитикой или сторонними виджетами?
Что работает быстрее: секвенция или видео?
Имеет ли смысл настраивать CDN?
Нормально ли, что показатели периодически скачут, хотя вы ничего не делали?
И другие подобные моменты из практики. Конечно, мы не сможем охватить в одной статье все подобные моменты, но постараемся коснуться наиболее распространенных проблем, чуть более сложных, чем: «На страницу загрузили не сжатую картинку на 20 мегабайт» или «Забыли на сервере включить GZIP».
Еще несколько лет назад многие рекомендации не имели особого смысла кроме тех случаев, когда загрузка сайта была настолько долгой, что не требовались специальные метрики, чтобы это понять. Но в связи с последними изменениями в политике поисковой выдачи Google оптимизация приобретает бОльший вес. Поэтому давайте для начала кратко пробежимся по последним изменениям от Google PageSpeed.
Изменения в Google PageSpeed
В конце 2018 старый механизм PageSpeed заменили оценками и аналитикой Lighthouse. И этот новый инструмент также встроен в Google Chrome. Основное отличие от предыдущих версий — баллы, которые теперь присуждаются не только за выполнение рекомендаций, но и непосредственно за скорость. Загрузка страницы начала оцениваться по нескольким временным параметрам:
в какой момент времени после начала загрузки становится виден контент;
когда можно взаимодействовать со страницей — кликать или вводить данные;
насколько медленно это все грузится, и когда все операции будут завершены.
Полученные значения характеристик потом сравниваются с другими сайтами из базы, которые недавно проходили проверку, и превращаются в баллы. Именно из-за «средней температуры по палате» полученные баллы могли двигаться в ту или иную сторону с течением времени. Кроме того, возможно изменение коэффициентов в алгоритме подсчета баллов, изменение параметров «тестовых устройств», да и вообще, даже в серии замеров, которые идут подряд друг за другом, результат может отличаться на несколько баллов, хотя ничего не менялось.
Вот как написано на официальном сайте:
«На результат измерения скорости загрузки в разной степени влияет множество факторов. Основные из них — это доступность локальной сети, доступность аппаратных средств клиента и наличие конфликтов при доступе к ресурсам клиента.»
На самом деле, повлиять может все что угодно, поэтому для более релевантной оценки, рекомендуется сделать не 1 замер, а 3-5 в течение дня в разное время суток.
В 2020-ом произошло обновление движка Lighthouse 6, на базе которого работает PageSpeed Insights. В Lighthouse 6 разработчиками было добавлено несколько значительных изменений. Появились новые метрики, которые ощутимо влияют на общую оценку быстродействия страницы. Обновились аудиты для «доступности» и Javascript.
Ключевые метрики:
Largest ContentFul Paint (LCP) — отрисовка крупного контента. Время, за которое большая часть контента отрисовывается на экране. Желательное время до 2,5 секунд, приемлемое — до 4 секунд.
First Input Delay (FID) — задержка между первым действием пользователя и реакцией браузера. Желательное время до 100 мс, приемлемое — до 300 мс.
Cumulative Layout Shift (CLS) — совокупный сдвиг макета. Показывает оценку визуальной стабильности страницы. Нацелено на борьбу с всплывающей рекламой и блоками, которые появляются и пропадают без действий со стороны пользователя. Если изображение «скачет», то показатели падают. Нормальное значение до 0,1, допустимое — до 0,25.
И вот эти показатели из отчета входят в Core Web Vitals. А значит, метрики будут существенно влиять на ранжирование поисковой выдачи. Важное условие — как минимум 75% страниц сайта должны соответствовать нижним границам этих трёх показателей. Иначе сайт будет признан Google плохо оптимизированным. Но при этом сами показатели имеют разные веса в учете суммарной оценки, например, LCP или FID влияют на итоговую сильнее, чем CLS.
В ноябре 2020 года Google подтвердил, что Core Web Vitals станет фактором ранжирования с мая 2021 года. Рекомендации как и раньше есть, но теперь они напрямую не связаны с баллами. Совершенно не факт, что следование рекомендациям улучшит ситуацию, так как выполнение некоторых из них может ухудшить ситуацию или быть неприемлемым с точки зрения поддержки различными браузерами. Ранее частенько встречалась ситуация, когда очевидно тормозящие сайты выдавали отличные оценки, а быстрые сайты оценивались плохо. Теперь с изменением алгоритмов становятся важными именно скорость и восприятие пользователем. Все маркетинговые погремушки, которые так любят запихнуть на первый экран, теперь неизбежно будут ухудшать оценку, как ты их не отлаживай. Поэтому рекомендация на будущее: на странице, особенно на первом экране, должен быть только максимально необходимый контент, желательно с минимумом анимаций и сторонних включений.
Как улучшить производительность?
Итак, что мы можем сделать для улучшения общей картины производительности?
Широко рекомендуется использовать асинхронную загрузку. При этом следует помнить, что асинхронный и «не влияющий на скорость загрузки» — разные вещи. Да, он позволяет загружать ресурсы параллельно, не блокируя какой-то из них, но общее время все равно увеличивается. Далее, если код вы уже вынесли вниз страницы, то добавление асинхронности ничего в плане блокирования загрузки контента не изменит. Так что асинхронность не панацея, а лишь способ частично улучшить ситуацию. Намного эффективнее — это использовать как можно меньше кода в целом, например, загружать не всю библиотеку, а только те компоненты, которые вам необходимы конкретно на этой странице.
Отсюда вытекает, что билд лучше дробить на составные части, дабы в дальнейшем мы могли подключать и задействовать только те вещи, которые нужны только на этой странице, более того, даже такой код лучше разбить на 2 части. Первая — это «критические» вещи, отвечающие за каркас вашего приложения и наполнение первого экрана. Вставляем их прямо в код. Все остальное ниже, а интерактив, требующий действий пользователя, вообще в конец страницы.
Особенно часто проблемы вызывает аналитика и прочие подобные вещи подключаемые через GTM. Сам по себе GTM страницу не замедляет, замедляет то, что вы кладете в контейнер, более того, подключение тех же сценариев непосредственно в код сайта, наоборот, может ситуацию ухудшить. Вариантов действий тут не так много, например:
Можно настроить кэширование на своем сервере и обновлять по крону, но помимо банального неудобства такого решения оно может ничего и не улучшить. А вот настройка самого контейнера на загрузку сценариев по событию способно поправить ситуацию. Нас интересует, естественно, отложенная загрузка по событию. В идеале подгрузку таких вещей стоило бы отложить до полной загрузки страницы, но аналитики скорее всего будут против, так как, например, если пользователь уйдет с сайта до момента полной загрузки, они не получат вообще никакой информации. Но, как минимум, стоит ориентироваться на событие DOM Ready только для сбора наиболее критичных метрик.
Можно использовать service worker, он будет кешировать часть или всё содержимое HTML-страницы. Сервис обновляет кеш только при каких-либо изменениях на странице.
Указывайте размеры изображений и видео в тэгах, это опять полезно. Если изображения адаптивные, указывайте хотя бы пропорции (например, 16:9).
Хочется обратить внимание, что рекомендация: сводить все изображения в спрайт, сейчас скорее вредит, чем помогает. Гораздо больше пользы принесет отложенная или «ленивая загрузка» не попадающих в текущую область видимости. Помните, что «ленивую загрузку» можно применять и для видео. Недавно я подробнее рассказывал об анимации. Во-первых, для видео, которое не нужно автоматически прокручивать, используйте атрибут preload="none". Для видео, которое используется в качестве анимаций, указывайте атрибут poster="" и используйте код JavaScript, аналогичный примерам отложенной загрузки изображений на основе Intersection Observer.
Некоторые необязательные вещи можно вообще вынести со страницы, подтягивать их только после действий пользователя. Это также относится и к скриптам, выполняющим специфические задачи, вроде: обращения к сторонним ресурсам, перестроения графиков данных или капчи. Это позволит нам не грузить лишние строки, а также уберет пункты из показателя «неиспользуемый код».
Можно применить web-workers — специальное API-решение, позволяющее загружать JavaScript, который не связан с пользовательским интерфейсом в фоновом режиме. Можно отказаться от отрисовки тяжелого содержимого в браузере в пользу серверного рендеринга. Так вы сократите время ответа сервера. Держите проект на быстром хостинге или VPS/VDS, мониторьте потребление ресурсов, оптимизируйте базу данных, настройте кеширование. Перейдите на новую версию PHP. Версия 7.3 работает в 3-4 раза быстрее старых редакций.
Используйте протокол http/2, он позволяет быстрее и эффективнее передавать данные между браузером и сервером. В отличие от http/1 он не создает отдельные соединение для загрузки каждого файла, а загружает их параллельно. Таким образом, http/2 уменьшает нагрузку на сервер и экономит его ресурсы.
Если ваша аудитория не ограничивается одним географическим регионом, тогда настройте CDN. Распределённая сеть доставки контента снижает нагрузку на хостинг благодаря распределению файлов на несколько источников. Изображения и видео являются самым ресурсоемким контентом, поэтому можно подгружать их через CDN или перенести на поддомен.
Что касается предпочтительности той или иной анимации, то все упирается в качество изображения и продолжительность. В силу наиболее продвинутых алгоритмов сжатия, видео предпочтительнее использования секвенции. Кроме того, в самом видео предпочтительно использовать новый формат WebM, поскольку он специально разработан для потоковой передачи через интернет. WebM намного меньше по сравнению с MP4. А такие решения как гиф-анимация вообще вызывают предупреждение PageSpeed с просьбой использовать более современные форматы. Выбор же между WebGL или видео для анимаций уже сильно неоднозначен, предсказать результат заранее не получится. В целом следует исходить из соотношения веса решений к их продолжительности.
Также иногда возникает вопрос: на некоторых сайтах сперва изображения не очень четкие, а потом они прогружаются, это же ускорение работы сайта? Визуально — да, но по факту — нет. Для подобной технологии мы сперва вынуждены грузить маленькие по весу изображения, а потом асинхронно подтягивать качественные. Это не частичная загрузка большого файла «побыстрей», а дополнительная предзагрузка маленького. Для пользователя это неплохо, а вот метрики таким не улучшить.
Не забываем про шрифты, помимо использования подходящих форматов, рекомендуется использовать rel="preload" как и для скриптов.
Также в стилях стоит указать font-display:swap; — это даст возможность использовать встроенные шрифты в ожидании загрузки основного.
Частым кейсом проблем со смещением макета, не связанным с всплывающей рекламой, являются слайдеры и плавающие сайдбары. В первом случае проблема связана с тем, что в странице создана только обертка слайдера, а все внутренности и размеры задаются уже после инициализации скрипта, что вызывает изменение размеров и пересчет координат всей страницы, поэтому не забывайте заранее указывать в стилях размеры нужного блока. А для плавающих сайдбаров вместо изменения значений с помощью скриптов лучше используйте свойство position: sticky.
Надеюсь, что статья была вам полезна, и будет круто, если вы поделитесь в комментариях примерами проблем и успешного их решения, которые я не упоминал в статье.