INP плохой — клики на сайте отвечают медленно
Interaction to Next Paint > 200ms. Тяжёлый JS, большой DOM, слишком много обработчиков.
Симптом
- Время реакции на клик превышает 200 мс, видимый в DevTools → Performance → INP > 200 мс.
- Пользователь видит задержку между нажатием кнопки и появлением результата (появление модального окна, переход на другую страницу, изменение текста).
- Вкладка «Main» в профайлере показывает длительные фрагменты JavaScript‑потока (long tasks).
- В консоли браузера часто появляются предупреждения о «long tasks» (Task timed out).
- На мобильных устройствах страница «зависает» более 1 секунды после клика.
- В отчётах Lighthouse/Chrome User Experience Report указана высокая метрика INP.
Причина
-
Тяжёлый JavaScript
- Большой объём кода, который загружается сразу (не‑ленивый импорт).
- Синхронные операции (heavy loops, DOM‑манипуляции, парсинг больших JSON‑файлов).
- Неоптимизированные библиотеки, которые блокируют основной поток. -
Большой DOM
- Слишком много элементов, особенно вложенных, что увеличивает время рендеринга и пересчёта стилей.
- Частые изменения структуры DOM (добавление/удаление узлов) в ответ на клик. -
Много обработчиков событий
- Один и тот же элемент имеет десяткиaddEventListener.
- Обработчики регистрируются в цикле, создавая «обёртки» и лишние вызовы. -
Синхронные запросы
-fetch/XMLHttpRequestв обработчике клика, которые блокируют UI‑поток до завершения. -
Плохая оптимизация рендеринга
-requestAnimationFrameне используется, а вместо этогоsetTimeout/setInterval.
- Необъёмные стили, которые пересчитываются каждый раз при клике.
Как проверить
-
DevTools → Performance → Record
- Запишите сессии с кликом.
- Посмотрите длительность задач (Long Tasks) и их количество.
- Оцените время до первого «paint» после клика. -
Lighthouse
- Запустите Lighthouse в режиме «Performance» и посмотрите раздел «Interaction to Next Paint». -
Chrome User Experience Report
- Откройте https://web.dev/innp/ и посмотрите реальные показатели INP для вашего сайта. -
Проверка JavaScript
- В DevTools → Sources → Coverage: включите покрытие и посмотрите, какие файлы загружаются и сколько кода реально исполняется.
- В профайлере → JavaScript →Event Listenersпросмотрите количество обработчиков на элементе. -
Проверка DOM
- В DevTools → Elements →:nth-childи:nth-of-typeпосмотрите глубину вложенности.
- Считайте количество узлов:document.querySelectorAll('*').length. -
Проверка запросов
- В Network вкладке посмотрите, какие запросы отправляются в обработчике клика и сколько времени они занимают.
Решение
1. Разделить JavaScript на чанки
// Вместо
import heavyLib from './heavyLib.js';
// используйте
import(/* webpackChunkName: "heavyLib" */ './heavyLib.js')
.then(module => {
// обработка после загрузки
});
Lazy‑load скрипты, которые нужны только после клика.
2. Уменьшить размер DOM
- Удалите неиспользуемые элементы.
- Сведите к минимуму вложенные
div. - Используйте
DocumentFragmentдля пакетных вставок.
const frag = document.createDocumentFragment();
for (const item of items) {
const el = document.createElement('div');
el.textContent = item;
frag.appendChild(el);
}
container.appendChild(frag);
3. Оптимизировать обработчики событий
- Event delegation: один обработчик на родительском элементе.
document.body.addEventListener('click', function (e) {
if (e.target.matches('.btn')) {
handleClick(e.target);
}
});
- Удаляйте ненужные слушатели после использования.
4. Перенести тяжёлые расчёты в Web Worker
// worker.js
onmessage = function(e) {
const result = heavyCalculation(e.data);
postMessage(result);
};
// main.js
const worker = new Worker('worker.js');
worker.postMessage(data);
worker.onmessage = function(e) {
updateUI(e.data);
};
5. Использовать requestAnimationFrame для анимаций
function animate() {
// обновление позиции
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
6. Минимизировать синхронные запросы
- Перенесите
fetchвasyncфункцию и используйтеawait. - Кешируйте ответы в
localStorageилиIndexedDB.
async function onClick() {
const data = await fetchData(); // не блокирует UI
render(data);
}
7. Сократить пересчёт стилей
- Добавьте класс, а не меняйте стили inline.
- Используйте
will-changeдля элементов, которые будут анимироваться.
.button {
will-change: transform;
}
8. Проверить после изменений
- Снова запустите Performance‑запись и убедитесь, что INP < 200 мс.
- Проверьте Lighthouse, чтобы убедиться, что метрика улучшилась.
Связанные
Не хотите разбираться сами?
Запустите технический аудит сайта за 5 минут — получите PDF-отчёт с разбором всех 64 параметров и конкретными точками роста. Или закажите комплексное SEO-продвижение сайта — починим всё это и возьмём на себя дальнейшую оптимизацию.