Показатели Core Web Vitals — Примеры

Пример 1 — Главное изображение отложено и без размеров

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

<img src="/hero.avif" loading="lazy" alt="Мастерская на закате" />

Решение

<link rel="preload" as="image" href="/hero.avif" fetchpriority="high" />
<img src="/hero.avif" width="1600" height="900" alt="Мастерская на закате" />

Largest Contentful Paint измеряет, когда появляется самый большой видимый элемент, а loading="lazy" намеренно откладывает именно этот элемент. Эта директива предназначена для изображений ниже первого экрана, где она экономит трафик, не роняя показатель. Предзагрузка и повышенный приоритет заставляют файл обнаружиться и скачаться раньше — это обычное единичное исправление позднего главного изображения. Cumulative Layout Shift измеряет движение, которого пользователь не вызывал, а изображение без объявленных размеров не резервирует места, поэтому каждая строка под ним сдвигается вниз при его появлении. width и height дают браузеру соотношение сторон для резервирования, тогда как отображаемый размер по-прежнему задаёт CSS. Два атрибута, два показателя.

Пример 2 — Обработчик, блокирующий каждое действие

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

button.addEventListener("click", () => {
  const matches = records.filter((r) => r.name.includes(query));
  render(matches);            // 400мс работы основного потока одной задачей
});

Решение

button.addEventListener("click", async () => {
  const matches = records.filter((r) => r.name.includes(query));

  for (let i = 0; i < matches.length; i += 100) {
    render(matches.slice(i, i + 100));
    await new Promise((resolve) => setTimeout(resolve, 0));
  }
});

Interaction to Next Paint измеряет задержку между нажатием и ближайшим кадром, который нарисует браузер. Задача в несколько сотен миллисекунд держит основной поток, так что ничего не может быть обработано до её конца — включая действия, не имеющие отношения к этому обработчику. Поэтому кажется, что зависла вся страница, а не одна кнопка. Уступая управление между кусками, вы возвращаете его браузеру, чтобы он мог отрисовать и разобрать ввод, и рендерер затем рисует постепенно, а не один раз в конце. Измерение, подтверждающее исправление, — дорожка основного потока в записи, а не время настенных часов в цикле.