Фронтенд3 мин чтения

INP на практике: как найти и исправить медленные клики

Interaction to Next Paint измеряет, сколько пользователь ждёт реакции страницы. Показываю, как измерить INP в поле и разбить длинные задачи.

inp

Иллюстративное фото: Unsplash

С марта 2024 года Interaction to Next Paint — одна из трёх Core Web Vitals, она заменила FID. Разница принципиальная: FID измерял только задержку первого взаимодействия, а INP смотрит на все клики, касания и нажатия клавиш за весь визит — и показывает одно из худших.

Что именно измеряет INP

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

  • Input delay — время, пока браузер вообще начнёт обрабатывать событие, потому что главный поток занят чем-то другим.
  • Processing time — время выполнения ваших обработчиков.
  • Presentation delay — пересчёт стилей, layout и отрисовка следующего кадра.
ОценкаINP (75-й перцентиль)
Хорошо≤ 200 мс
Нужно улучшить200–500 мс
Плохо> 500 мс

Измеряйте в поле, а не только в лаборатории

Lighthouse не кликает за пользователя, поэтому настоящий INP не покажет. Нужны данные реальных сессий. Проще всего — библиотека web-vitals со сборкой attribution, которая сразу говорит, какой элемент и какая фаза подвели.

import { onINP } from 'web-vitals/attribution';

onINP(({ value, attribution }) => {
  const { interactionTarget, inputDelay, processingDuration, presentationDelay } = attribution;
  navigator.sendBeacon('/vitals', JSON.stringify({
    value, interactionTarget, inputDelay, processingDuration, presentationDelay,
  }));
});

Через неделю сбора обычно видно, что проблему создают несколько конкретных элементов: фильтр в магазине, кнопка «в корзину», меню на телефоне.

Разбейте длинные задачи

Самая частая причина плохого INP — обработчик, который делает всё сразу: обновляет состояние, пересчитывает список, отправляет аналитику. Браузер не может отрисовать кадр, пока это не закончится. Решение: сразу сделать то, что видно, а остальное отдать по частям.

function yieldToMain() {
  if ('scheduler' in window && 'yield' in scheduler) return scheduler.yield();
  return new Promise((resolve) => setTimeout(resolve, 0));
}

button.addEventListener('click', async () => {
  showSpinner();            // пользователь сразу видит реакцию
  await yieldToMain();
  applyFilters();
  await yieldToMain();
  sendAnalytics();
});

Совет Панель Performance в Chrome DevTools теперь показывает взаимодействия на отдельной дорожке и помечает те, что длиннее 200 мс. Запишите клик и ищите красные треугольники.

Быстрые победы

  • Отложите сторонние скрипты (чат, тепловые карты) до момента после взаимодействия или простоя.
  • Уменьшите DOM — тысячи узлов делают каждый layout дороже.
  • Используйте content-visibility: auto для секций ниже первого экрана.
  • Не читайте размеры (offsetHeight) сразу после изменения стилей в цикле — это вызывает синхронный layout.

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

STOS

Блог о фронтенде, DevOps и инструментах

Сделано на WordPress: своя блочная тема, Polylang, Yoast SEO. Посмотреть админку WordPress в Playground ↗

STOS — вымышленный блог, концепт-проект для портфолио. Автор и тексты — примеры.