Зачем улучшать INP
INP (Interaction to Next Paint) — метрика, которая измеряет отзывчивость страницы: сколько времени проходит от действия пользователя (клик, нажатие клавиши, тап) до визуального обновления экрана. Google заменил INP на место FID (First Input Delay) в марте 2024 года, и теперь это официальный Core Web Vital.
Разница принципиальная. FID измерял задержку только первого взаимодействия. INP оценивает все взаимодействия за время сеанса и берёт наихудшее (с поправкой на выбросы). Это значит, что страница может быстро реагировать на первый клик, но тормозить при открытии меню или переключении фильтра — и INP это покажет.
Целевые значения INP
- Хорошо: до 200 мс — пользователь воспринимает реакцию как мгновенную
- Требует улучшения: 200-500 мс — заметная задержка, нужна оптимизация
- Плохо: больше 500 мс — страница ощущается как тормозящая, пользователи уходят
По моему опыту, проблемы с INP чаще встречаются на страницах с тяжёлым JavaScript: каталоги с фильтрами, SPA-приложения, страницы с большим количеством интерактивных элементов. Простые контентные страницы обычно укладываются в норму без дополнительной оптимизации.
Пошаговая инструкция: диагностика и оптимизация INP
Шаг 1. Измерьте текущий INP
Начните с PageSpeed Insights. В блоке полевых данных (CrUX) посмотрите значение INP. Важно: INP — метрика, которую невозможно полноценно измерить лабораторно, потому что она зависит от реальных взаимодействий пользователей. Lighthouse показывает приблизительную оценку Total Blocking Time (TBT) как лабораторный аналог, но это не одно и то же.
Шаг 2. Определите медленные взаимодействия
Установите расширение Web Vitals для Chrome. Включите режим логирования в консоль. Теперь при каждом клике, тапе или нажатии клавиши в консоли DevTools появится запись с временем обработки. Пройдитесь по всем интерактивным элементам страницы: кнопки, ссылки, фильтры, формы, модальные окна. Запишите, какие взаимодействия превышают 200 мс.
Шаг 3. Проанализируйте JavaScript через Performance-профайлер
В Chrome DevTools откройте вкладку Performance. Нажмите Record, выполните проблемное взаимодействие, остановите запись. На таймлайне найдите момент клика — он отмечен маркером Event. Раскройте секцию Main — здесь видно, какие JavaScript-функции выполнялись и сколько времени заняли. Длинные жёлтые блоки (Long Tasks) — главные кандидаты на оптимизацию.
Шаг 4. Разбейте длинные задачи
Браузер работает в одном потоке. Если JavaScript-функция выполняется 300 мс подряд — браузер не может обновить экран, пока она не завершится. Решение — разбить длинную задачу на части с помощью scheduler.yield() (современный API) или setTimeout(0) (работает везде). После каждого фрагмента браузер получает возможность обработать пользовательский ввод и обновить экран.
Шаг 5. Оптимизируйте обработчики событий
Проверьте, что делают обработчики кликов и других событий. Частые проблемы:
- Синхронный рендеринг больших списков — замените на виртуализацию или рендеринг порциями
- Тяжёлые вычисления в обработчике — перенесите в Web Worker
- Обращение к DOM в цикле — соберите изменения и примените одним batch
- Forced reflow: чтение layout-свойств (offsetHeight) после записи — разделите чтение и запись
Основные причины плохого INP
Тяжёлые сторонние скрипты
Аналитика, чаты, рекомендательные виджеты, A/B-тесты — каждый скрипт добавляет задачи в основной поток. Проверьте во вкладке Performance, сколько времени занимают сторонние скрипты. Решение: загружайте некритичные скрипты отложенно, используйте Partytown для переноса аналитики в Web Worker, удалите неиспользуемые скрипты.
Чрезмерное количество DOM-элементов
Если на странице больше 1500 DOM-элементов, каждое обновление DOM занимает больше времени. При клике на фильтр каталога браузер может перерисовывать тысячи элементов. Решение: уменьшите DOM до необходимого минимума, используйте виртуальный скролл для длинных списков, скрывайте невидимые элементы через content-visibility: auto.
Неоптимизированный рендеринг фреймворков
React, Vue, Angular — все JavaScript-фреймворки при обновлении состояния запускают цикл рендеринга. Если компоненты не мемоизированы или состояние обновляется слишком часто — каждый клик вызывает каскад перерисовок. Используйте React.memo, useMemo, computed properties и другие инструменты фреймворка для минимизации ререндеров.
Большие бандлы JavaScript
Если весь JavaScript загружается одним файлом в 500 КБ — парсинг и компиляция занимают значительное время. При первых взаимодействиях скрипт может ещё не быть полностью обработан. Используйте code splitting: загружайте только тот код, который нужен для текущей страницы. Критический JavaScript — до 150 КБ (gzip).
Типичные ошибки
- Ориентироваться на TBT вместо INP. Total Blocking Time в Lighthouse — полезный индикатор, но не замена INP. TBT измеряет блокировку при загрузке, INP — при взаимодействии. Страница может иметь отличный TBT и плохой INP, если тяжёлый код выполняется при кликах.
- Оптимизировать только первый клик. INP учитывает все взаимодействия за сеанс. Тестируйте полный пользовательский сценарий: открытие меню, применение фильтров, отправка формы, переключение табов.
- Добавлять debounce там, где нужен мгновенный отклик. Debounce задерживает выполнение — пользователь нажимает кнопку и ничего не происходит 300 мс. Для кликов лучше дать мгновенный визуальный отклик (анимация, лоадер) и выполнять тяжёлую работу в фоне.
- Игнорировать input и keydown события. INP считает не только клики. Если на сайте есть поле поиска с автодополнением — каждое нажатие клавиши может создавать тяжёлое взаимодействие.
Что проверить в итоге
- INP в полевых данных PageSpeed Insights ниже 200 мс для мобильных и десктопных
- В Search Console нет страниц с плохим INP
- Все интерактивные элементы реагируют на клик быстрее 200 мс (проверено через Web Vitals Extension)
- Нет Long Tasks дольше 50 мс в обработчиках событий (проверено через Performance-профайлер)
- Сторонние скрипты загружаются отложенно и не блокируют основной поток
- DOM-дерево не превышает 1500 элементов на сложных страницах
- JavaScript-бандл разбит на чанки, критический код до 150 КБ