Привет, друзья!
Представляю вашему вниманию перевод этой замечательной статьи, представляющей собой коллекцию лучших практик, которые по мнению команды Chrome DevRel являются наиболее эффективными способами улучшения показателей Core Web Vitals.
Core Web Vitals — это часть метрик Web Vitals, используемых для оценки веб-страниц и включенных во все инструменты Google. Владельцы сайтов должны учитывать эти метрики. Каждый показатель Core Web Vitals представляет собой отдельный аспект опыта взаимодействия пользователя с сайтом, измеряемый в полевых условиях и отражающий реальные действия по достижению критически важного результата, ориентированного на пользователя.
Web Vitals — это инициатива Google, цель которой — предоставить единое руководство по сигналам качества, необходимым для обеспечения хорошего взаимодействия с пользователем в Интернете (источник).
Интересно? Тогда прошу под кат.
Текущий набор метрик Core Web Vitals фокусируется на трех аспектах взаимодействия с пользователем: скорости загрузки страниц сайта, интерактивности и визуальной стабильности, и включает следующие показатели (и их соответствующие пороговые значения):
- Largest Contentful Paint (LCP) — Скорость загрузки основного контента: измеряет производительность загрузки. Чтобы обеспечить удобство работы пользователей, показатель LCP должен быть в пределах 2,5 секунды от начала загрузки страницы.
- First Input Delay (FID) — Время ожидания до первого взаимодействия с контентом: измеряет интерактивность. Показатель FID у страниц не должен превышать 100 миллисекунд.
- Cumulative Layout Shift (CLS) — Совокупное смещение макета: измеряет визуальную стабильность. Показатель CLS не должен превышать 0,1.
Недавно в CWV была добавлена еще одна метрика — Interaction to Next Paint (INP) — Интерактивность до следующей отрисовки: измеряет интерактивность на протяжении всего времени посещения пользователем страницы. Данная метрика пока считается экспериментальной. Показатель INP не должен превышать 200 миллисекунд.
Хотя показатели Core Web Vitals являются критически важными для понимания и обеспечения хорошего взаимодействия с пользователем, существуют и другие важные метрики.
Они часто служат в качестве промежуточных или дополнительных показателей для Core Web Vitals, и помогают полнее охватить взаимодействие с пользователем или диагностировать конкретную проблему.
Например, метрики Time to First Byte (TTFB) — Время до первого байта и First Contentful Paint (FCP) — Первая отрисовка контента являются критически важными аспектами загрузки и полезны для диагностики проблем с LCP (медленное время отклика сервера или ресурсы, блокирующие рендеринг).
Аналогично, такие показатели, как Total Blocking Time (TBT) — Общее время блокировки и Time to Interactive (TTI) — Время до интерактивности, являются крайне важными метриками для выявления и диагностики потенциальных проблем с интерактивностью, которые могут повлиять на FID. Однако они не входят в набор Core Web Vitals, потому что не поддаются измерению в полевых условиях и не отражают результаты, ориентированные на пользователя.
Приведенный ниже список рекомендаций по повышению веб-производительности акцентирует внимание на трех вещах:
- наибольшее влияние в реальном мире;
- актуальность и применимость к большинству сайтов;
- реализуемость большинством разработчиков.
Largest Contentful Paint (LCP)
На сегодняшний день лишь около половины сайтов в Интернете удовлетворяет пороговому значению LCP.
Ресурс LCP должен находиться в HTML
Согласно Web Almanac 2022, подготовленного HTTP Archive, 72% мобильных страниц содержат изображение в качестве элемента LCP. Это означает, что быстрая загрузка этих изображений является критически важной для LCP.
Однако скорость загрузки изображения — это только одна часть задачи. Другой частью является минимальное время до начала загрузки изображения.
39% источников указанных выше изображений являются недостижимыми в HTML. Другими словами, их URL не были обнаружены в стандартных атрибутах HTML (таких как <img src="...">
или <link rel="preload" href="...">
), которые позволяют браузеру быстро обнаружить и начать загрузку изображения.
Если URL изображения недостижим в HTML, значит, страница должна ждать полной загрузки файлов CSS или JavaScript, их разбора (парсинга) и обработки до начала загрузки изображения.
Общая рекомендация: если изображение является элементом LCP, его URL должен находиться в HTML. Этого можно достичь следующим образом:
-
загрузка изображения с помощью элемента
<img>
с атрибутомsrc
илиsrcset
. Не используйте нестандартные атрибуты вродеdata-src
, которые требуют JS для рендеринга. 9% проанализированных страниц скрывают свои LCP изображения за атрибутомdata-src
; - используйте server-side rendering (SSR) — рендеринг на стороне сервера вместо client-side rendering (CSR) — рендеринга на стороне клиента, поскольку SSR возвращает страницу целиком, включая изображения. CSR требует JS для рендеринга страницы, включая изображения;
-
если изображение запрашивается из внешнего файла CSS или JS, для его предварительной загрузки можно использовать тег
<link rel="preload">
. Обратите внимание: ссылка на изображение во встроенных стилях (inline styles) не обнаруживается сканером предварительной загрузки.
Обеспечение достижимости изображения LCP в разметке приводит не только к улучшению производительности, но также открывает возможность для приоритизации ресурса.
Ресурс LCP должен быть приоритетным
Ресурс LCP должен быть не только достижим в HTML, он также должен быть приоритетным по сравнению с другими менее важными ресурсами.
Например, если изображение LCP присутствует в HTML, но в <head>
документа имеется множество тегов <script>
перед тегом <img>
, может пройти много времени до начала загрузки изображения.
Простейшим способом решения этой проблемы является установка тегу <img>
или <link>
, загружающему изображение LCP, нового атрибута fetchpriority="high". Это сообщает браузеру о необходимости первоочередной загрузки указанного ресурса.
Согласно Web Almanac только 0,03% проанализированных страниц используют преимущества нового интерфейса. Хотя атрибут fetchpriority
в настоящее время поддерживается только браузерами на основе Chromium, его можно рассматривать как прогрессивное улучшение, которое будет просто игнорироваться другими браузерами.
Для браузеров, не поддерживающих fetchpriority
, единственным способом решения проблемы является размещение тега <link rel="preload">
перед скриптами в <head>
или перемещение скриптов в конец <body>
после тега <img>
.
Другим важным аспектом приоритизации ресурса LCP является отсутствие вещей, понижающих его приоритет, таких как атрибут loading="lazy"
. На сегодняшний день 10% страниц устанавливают этот атрибут изображениям LCP. Чаще всего это связано с использованием инструментов для оптимизации изображений, которые применяют "ленивую" (отложенную) загрузку (lazy-loading) ко всем изображениям.
Еще одним способом повышения приоритета ресурса LCP является задержка загрузки некритических ресурсов (их отложенная загрузка). Например, загрузка скриптов, которые не участвуют в формировании пользовательского интерфейса (такие как аналитика или виджеты социальных сетей), может быть отложена до возникновения события load
, чтобы они не уменьшали пропускную способность сети для важных ресурсов (таких как изображения LCP).
Лучшие практики по быстрой загрузке ресурса LCP с высоким приоритетом могут быть сведены к следующему:
-
добавляем атрибут
fetchpriority="high"
к тегу<img>
, загружающему изображение LCP. Если ресурс загружается с помощью тега<link rel="preload">
, ему также можно установитьfetchpriority="high"
; -
никогда не устанавливаем атрибут
loading="lazy"
тегу<img>
, загружающему изображение LCP. Это снижает приоритет загрузки изображения, т.е. откладывает начало его загрузки; - откладываем загрузку некритических ресурсов. Перемещаем их в конец документа, используем нативные механизмы ленивой загрузки для изображений и фреймов или загружаем их асинхронно с помощью JS.
Использование CDN для оптимизации TTFB документа и ресурса
Последним кусочком пазла является обеспечение максимально быстрого ответа на первоначальный запрос документа. Браузер не может начать загрузку ресурсов до получения первого байта ответа на такой запрос.
Это время известно как TTFB и лучшим способом его уменьшения является следующее:
- обслуживать контент максимально близко (географически) к пользователю;
- кэшировать контент для недавно запрошенных ресурсов для ускорения повторного ответа.
Лучшим способом решения этих задач является использование CDN. CDN распределяют ресурсы по нескольким серверам, которые находятся в разных местах земного шара. Это уменьшает расстояние, которое должен пройти ресурс до пользователя. CDN также, как правило, имеют отличные механизмы кэширования, которые могут быть кастомизированы и оптимизированы для нужд вашего сайта.
Многие разработчики используют CDN для обслуживания статических ресурсов. Однако CDN могут также обслуживать и кэшировать документы HTML, даже в случае их динамической генерации.
Согласно Web Almanac только 29% документов HTML обслуживаются CDN.
Несколько трюков по настройке CDN:
- рассмотрите возможность увеличения времени кэширования (обязательно ли контент должен быть всегда свежим? Или он может обновляться один раз в несколько минут?);
- рассмотрите возможность совместного кэширования нескольких ресурсов и их одновременного обновления;
- рассмотрите возможность использования граничных вычислений для сложной логики, выполняющейся на вашем сервере.
Возможность обслуживать контент с помощью граничных вычислений существенно снижает время ответа. Однако даже если вам необходимо обратиться к серверу для загрузки ресурса, CDN могут сильно помочь в оптимизации этого процесса.
Cumulative Layout Shift (CLS)
На сегодняшний день около четверти сайтов не удовлетворяют пороговому значению CLS.
Явные размеры контента
Сдвиг макета обычно происходит, когда существующий контент сдвигается после завершения загрузки другого контента. Таким образом, основным способом решения этой проблемы является резервирование места для загружаемого контента в количестве, максимально близком к необходимому (тому, который фактически будет занимать загруженный контент).
Одним из очевидных способов предотвращения сдвига макета является установка размеров изображения с помощью атрибутов width
и height
(или соответствующих свойств CSS). Однако согласно HTTP Archive 72% страниц содержат как минимум одно "безразмерное" изображение. Дефолтная высота таких изображений, устанавливаемая браузером, составляет 0px
, что может привести к заметному сдвигу макета после загрузки изображения и вычисления его размеров.
Важно помнить, что сдвиги макета вызываются не только изображениями. Они также могут вызываться контентом, который загружается после первоначального рендеринга страницы, например, рекламой или встроенными видео. Справиться с этим может помочь свойство aspect-ratio. Это относительно новое свойство CSS позволяет разработчикам определять соотношение сторон изображений и других элементов. Это, в частности, позволяет устанавливать динамическую ширину (например, на основе ширины области видимости), на основе которой браузер автоматически вычисляет соответствующую высоту.
Но что если нам неизвестны даже приблизительные размеры динамического контента? Установка свойства min-height
почти всегда лучше использования браузером дефолтной высоты пустого элемента, равной 0px
. min-height
не ограничивает высоту элемента, т.е. если окажется, что контент выше min-height
, его контейнер просто немного растянется.
bfcache
Браузеры используют механизм навигации под названием back/forward cache — bfcache для мгновенной доставки страницы из снимка памяти (memory snapshot) истории браузера.
bfcache — это серьезная оптимизация производительности браузера, которая существенно уменьшает сдвиг макета в процессе загрузки страницы. Представление bfcache стало "наибольшим" улучшением CLS в 2022 году.
Большое число сайтов не подходят для применения bfcache, поэтому они не могут воспользоваться этим бесплатным улучшением навигации.
Убедитесь, что ваши страницы подходят для применения bfcache. Chrome предоставляет тестировщик bfcache в инструментах разработчика. Скоро соответствующий инструмент появится в Lighthouse.
bfcache может улучшить не только CLS, но и другие метрики CWV.
Анимации и переходы
Другой распространенной причиной сдвигов макета является анимирование элементов. Например, баннеры куки или баннеры с другими уведомлениями, появляющиеся сверху или снизу, часто влияют на CLS. Даже если они не добавляют контент на страницу, их анимирование может влиять на CLS.
Исследования показывают, что CLS страниц, на которых анимируются свойства CSS, влияющие на макет, в среднем, на 15% хуже, чем CLS страниц, которые этого не делают. Одни свойства влияют на CLS хуже, чем другие. Например, очень негативно сказывается на CLS анимирование величины margin
или ширины border
.
Это не удивительно, поскольку каждый раз, когда анимируется свойство CSS, влияющее на макет, происходит сдвиг макета, и если эти сдвиги происходят позже 500 миллисекунд с начала взаимодействия пользователя со страницей, они ухудшают CLS.
Это справедливо даже в отношении элементов, изъятых из обычного потока документа. Например, элементы с position: absolute
, у которых анимируются свойства top
или left
, приводят к сдвигам макета. Однако если вместо top
или left
мы анимируем transform: translateX()
или transform: translateY()
, это не заставляет браузер пересчитывать макет и не приводит к его сдвигам.
Предпочтение анимации свойств CSS, которые могут обновляться браузером на уровне композиции (compositor thread), давно является лучшей практикой по повышению производительности, поскольку анимация выполняется в GPU, за пределами основного потока (main thread).
Общая рекомендация: никогда не анимируйте и не модифицируйте состояние элемента при переходе с помощью свойств CSS, которые вынуждают браузер обновлять макет страницы до тех пор, пока это не делается в ответ на действие пользователя (только не с помощью hover). По-возможности реализуйте анимацию и переходы с помощью свойства transition
.
First Input Delay (FID)
Interaction to Next Paint (INP) является потенциальной заменой FID, поэтому указанные ниже рекомендации применимы к обеим метрикам. INP является более строгой, чем FID за счет более длительного времени проведения измерений.
Длительные задачи
Задача — это любая работа, выполняемая браузером. Задачи включают рендеринг, формирование макета, парсинг, компиляцию и выполнение скриптов. Когда задачи становятся длительными (когда их выполнение длится более 50 миллисекунд), они блокируют основной поток и, как следствие, отклик страницы на действия пользователя.
Длительные (большие) задачи должны разбиваться на более короткие (мелкие). Это можно реализовать с помощью переключения на основной поток (например, с помощью async/await
) для более быстрого рендеринга обновлений и отклика на действия пользователя.
Другим способом является использование интерфейсов вроде isInputPending() и Scheduler. isInputPending
— это функция, которая возвращает логическое значение — индикатор того, что ожидается пользовательский ввод. Если возвращается true
, необходимо переключиться на основной поток для обработки пользовательского ввода.
Scheduler API — более продвинутая техника, позволяющая планировать задачи на основе системы приоритетов, принимающей во внимание то обстоятельство, видна ли пользователю выполняемая работа или же она выполняется в фоновом режиме.
Разделение длительных задач предоставляет браузеру больше возможностей для своевременной обработки действий пользователя и рендеринга обновлений.
Лишний JS
В настоящее время веб-приложения используют больше JS, чем когда бы то ни было. И в ближайшее время ситуация вряд ли изменится.
Несколько рекомендаций по уменьшению количества JS:
- используйте инструмент покрытия (coverage tool) инструментов разработчика для обнаружения неиспользуемого кода. Уменьшение количества ресурсов, загружаемых при запуске приложения, приводит к тому, что приложение тратит меньше времени на парсинг и компиляцию кода, что, в свою очередь, приводит к более плавному пользовательскому опыту (это особенно актуально для первого впечатления пользователя от взаимодействия с приложением);
- иногда код, помеченный как "неиспользуемый" инструментом покрытия, не используется при запуске приложения, но необходим для другого функционала. Такой код можно вынести в отдельную сборку с помощью разделения кода;
- при использовании менеджера тегов убедитесь, что теги периодически проверяются на предмет оптимизации или даже на предмет "используемости". Старые теги с неиспользуемым кодом могут быть удалены, что сделает менеджер тегов меньше и эффективнее.
Рендеринг больших обновлений
JS — это не единственная вещь, которая может влиять на отзывчивость приложения. Дорогой (с точки зрения вычислений) задачей может стать рендеринг — рендеринг больших обновлений может негативно влиять на скорость реакции приложения на действия пользователя.
Несколько рекомендаций на этот счет:
- не используйте
requestAnimationFrame()
для невидимой работы, т.е. работы, не связанной с обновлением пользовательского интерфейса. ВызовыrequestAnimationFrame()
обрабатываются на стадии рендеринга цикла событий. Когда на этой стадии выполняется слишком много работы, рендеринг обновлений может быть отложен; - сохраняйте ваш DOM маленьким. Размер DOM напрямую связан с объемом работы по формированию макета. Чем меньше DOM, тем меньше работы нужно выполнить для обновления макета;
- используйте CSS Containment. CSS Containment позволяет браузеру изолировать поддерево DOM (определенное в свойстве
contain
) от остальной части страницы и исключить его из процессов формирования макета и рендеринга без необходимости. Свойствоcontent-visibility
позволяет браузеру пропускать рендеринг элементов, находящихся за пределами области видимости.
Ссылки для дальнейшего изучения:
Надеюсь, что вы, как и я, узнали что-то новое и не зря потратили время.
Благодарю за внимание и happy coding!