INP (Interaction to Next Paint) — это метрика, предназначенная для измерения отклика страницы на пользовательские действия в реальном времени. Она отражает задержку между взаимодействием пользователя с элементом интерфейса и визуальной реакцией браузера. Основной поток (main thread) играет ключевую роль в формировании этой метрики, поскольку все события ввода и рендеринга проходят через него.
Основной поток браузера отвечает за:
Любая длительная задача в основном потоке блокирует обработку событий, что напрямую увеличивает задержку отклика на действия пользователя. Это критически важно для INP, так как даже кратковременные блокировки основного потока могут привести к высокой задержке между событием и визуальным откликом.
Длительные синхронные скрипты способны блокировать основной поток на десятки и сотни миллисекунд. Примеры:
function heavyComputation() {
let sum = 0;
for (let i = 0; i < 1e8; i++) {
sum += i;
}
return sum;
}
document.getElementById('button').addEventListener('click', () => {
heavyComputation(); // блокировка основного потока
});
В этом примере клик по кнопке запускает тяжёлую синхронную задачу, которая полностью блокирует основной поток. INP в этом случае фиксирует высокое значение, потому что визуальная реакция на клик задерживается до завершения цикла.
Основной поток обрабатывает очередь событий (Event Loop). Если в очереди много задач, то событие пользователя должно ждать своей обработки. Метрики INP особенно чувствительны к следующей ситуации:
Оптимизация INP требует минимизации времени, проведённого задачами в основном потоке, чтобы события обрабатывались как можно быстрее.
Каждое изменение DOM или стиля вызывает перерасчет стилей (recalculate style), layout и repaint. Эти операции выполняются в основном потоке и блокируют его, увеличивая задержку отклика:
Частые изменения DOM и стилевых свойств увеличивают нагрузку на основной поток и, следовательно, INP.
Библиотека Web Vitals предоставляет API для измерения INP:
import { getINP } from 'web-vitals';
getINP(metric => {
console.log(metric.name, metric.value, metric.delta);
});
metric.value — измеренное значение INP в
миллисекундах.metric.delta — изменение по сравнению с предыдущим
измерением.1. Разделение задач на мелкие блоки Разбивка тяжелых
вычислений на маленькие части с использованием
requestIdleCallback или setTimeout позволяет
не блокировать основной поток:
function chunkedComputation(items) {
let i = 0;
function processChunk() {
const chunkSize = 1000;
const end = Math.min(i + chunkSize, items.length);
for (; i < end; i++) {
// обработка элементов
}
if (i < items.length) {
requestIdleCallback(processChunk);
}
}
requestIdleCallback(processChunk);
}
2. Отложенная загрузка не критичных скриптов
Использование defer и async для скриптов
снижает нагрузку на основной поток в момент первичного рендеринга.
3. Минимизация повторных Layout и Reflow
transform и opacity вместо
width/height для анимаций,4. Оптимизация обработки событий
throttle или
debounce для scroll, mousemove и input.const button = document.querySelector('#button');
button.addEventListener('click', () => {
// Долгая операция
for (let i = 0; i < 1e7; i++) {}
console.log('Clicked!');
});
В таком случае INP фиксирует задержку на десятки миллисекунд. Решение — разделить операцию на блоки и использовать асинхронный подход:
button.addEventListener('click', () => {
setTimeout(() => {
for (let i = 0; i < 1e7; i++) {}
console.log('Clicked!');
}, 0);
});
Основной поток освобождается для обработки визуальных изменений и событий, что снижает INP.
Эффективное управление основным потоком напрямую влияет на пользовательский опыт, делая интерфейс отзывчивым и минимизируя задержки между действием и визуальной реакцией.