1. Введение
Интерактивная демонстрация и практическое занятие для изучения взаимодействия с последующим отрисовыванием (INP) .
Предварительные требования
- Знание разработки на HTML и JavaScript.
- Рекомендуется: ознакомиться с документацией INP .
Чему вы научитесь
- Как взаимодействие пользователей и обработка этих взаимодействий влияют на адаптивность страницы.
- Как уменьшить и устранить задержки для обеспечения бесперебойной работы пользователя.
Что вам нужно
- Компьютер, способный клонировать код с GitHub и запускать команды npm.
- Текстовый редактор.
- Для корректной работы всех функций измерения взаимодействия необходима последняя версия Chrome.
2. Настройка
Получите и запустите код.
Код находится в репозитории web-vitals-codelabs .
- Клонируйте репозиторий в терминале:
git clone https://github.com/GoogleChromeLabs/web-vitals-codelabs.git - Перейдите в клонированную директорию:
cd web-vitals-codelabs/understanding-inp - Установите зависимости:
npm ci - Запустите веб-сервер:
npm run start - Откройте в браузере страницу http://localhost:5173/understanding-inp/
Обзор приложения
В верхней части страницы расположен счетчик очков и кнопка увеличения . Классическая демонстрация реактивности и отзывчивости!

Под кнопкой расположены четыре измерительных элемента:
- INP: текущий показатель INP, который обычно отражает наихудшее взаимодействие.
- Взаимодействие: оценка последнего взаимодействия.
- FPS: количество кадров в секунду, отображаемое основным потоком страницы.
- Таймер: анимация работающего таймера, помогающая визуализировать рывки.
Значения FPS и Timer совершенно не обязательны для измерения взаимодействия. Они добавлены лишь для упрощения визуализации отзывчивости.
Попробуйте!
Попробуйте взаимодействовать с кнопкой «Увеличить» и понаблюдайте за ростом счета. Изменяются ли значения INP и «Взаимодействие» с каждым увеличением?
INP измеряет время, прошедшее с момента взаимодействия пользователя с сайтом до фактического отображения пользователем отрендеренного обновления.
3. Измерение взаимодействия с помощью инструментов разработчика Chrome.
Откройте инструменты разработчика из меню «Дополнительные инструменты» > «Инструменты разработчика» , щелкнув правой кнопкой мыши на странице и выбрав «Проверить элемент» , или с помощью сочетания клавиш .
Перейдите в панель «Производительность» , которая будет использоваться для измерения взаимодействия.

Далее, зафиксируйте взаимодействие на панели «Производительность».
- Нажмите кнопку записи.
- Взаимодействуйте со страницей (нажмите кнопку « Увеличить »).
- Остановите запись.
На появившейся временной шкале вы найдете дорожку «Взаимодействия» . Разверните ее, щелкнув по треугольнику в левой части экрана.

Появляются два интерактивных элемента. Увеличьте масштаб второго элемента, прокрутив его или удерживая клавишу W.

При наведении курсора на взаимодействие можно увидеть, что оно происходило быстро, не затрачивая времени на обработку , и сопровождалось минимальной задержкой ввода и задержкой отображения , точная продолжительность которых будет зависеть от скорости вашего компьютера.
4. Постоянные слушатели событий
Откройте файл index.js и раскомментируйте функцию blockFor внутри обработчика событий.
Полный код смотрите здесь: click_block.html
button.addEventListener('click', () => {
blockFor(1000);
score.incrementAndUpdateUI();
});
Сохраните файл. Сервер увидит изменения и обновит страницу.
Попробуйте снова взаимодействовать со страницей. Теперь взаимодействие будет заметно медленнее.
трассировка производительности
Сделайте еще одну запись на панели «Производительность», чтобы посмотреть, как это выглядит там.

То, что раньше было коротким взаимодействием, теперь занимает целую секунду.
При наведении курсора на взаимодействие обратите внимание, что почти все время тратится на "продолжительность обработки", то есть на время выполнения обратных вызовов обработчика событий. Поскольку блокирующий вызов blockFor полностью находится внутри обработчика событий, именно туда и уходит время.
5. Эксперимент: продолжительность обработки
Попробуйте различные варианты перестановки обработчиков событий, чтобы увидеть их влияние на INP.
Сначала обновите пользовательский интерфейс.
Что произойдет, если поменять местами вызовы JavaScript — сначала обновить пользовательский интерфейс, а затем заблокировать выполнение?
Полный код смотрите здесь: ui_first.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
blockFor(1000);
});
Вы заметили, что интерфейс появился раньше? Влияет ли порядок появления на результаты INP?
Попробуйте провести анализ взаимодействия и выявить возможные различия.
Отдельные слушатели
А что если перенести эту работу в отдельный обработчик событий? Обновлять пользовательский интерфейс можно в одном обработчике событий, а блокировать страницу — в другом.
Полный код: two_click.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('click', () => {
blockFor(1000);
});
Как это теперь выглядит на панели производительности?
Различные типы мероприятий
Большинство взаимодействий генерируют множество типов событий, от событий указателя или клавиши до событий наведения курсора, фокусировки/размытия, а также синтетических событий, таких как beforechange и beforeinput.
На многих реальных веб-страницах есть слушатели для самых разных событий.
Что произойдет, если изменить типы событий для обработчиков событий? Например, заменить один из обработчиков события click на pointerup или mouseup ?
Полный код смотрите здесь: diff_handlers.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('pointerup', () => {
blockFor(1000);
});
Обновление пользовательского интерфейса отсутствует.
Что произойдет, если удалить вызов обновления пользовательского интерфейса из обработчика событий?
Полный код смотрите здесь: no_ui.html
button.addEventListener('click', () => {
blockFor(1000);
// score.incrementAndUpdateUI();
});
6. Результаты эксперимента по продолжительности обработки.
Трассировка производительности: сначала обновите пользовательский интерфейс.
Полный код смотрите здесь: ui_first.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
blockFor(1000);
});
Проанализировав запись нажатия кнопки на панели производительности, можно увидеть, что результаты не изменились. Хотя обновление пользовательского интерфейса было инициировано до блокирующего кода, браузер фактически не обновил отображаемое на экране до завершения работы обработчика события, а это значит, что взаимодействие заняло чуть больше секунды.

Трассировка производительности: отдельные слушатели
Полный код: two_click.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('click', () => {
blockFor(1000);
});
Повторюсь, функциональной разницы нет. Взаимодействие по-прежнему занимает целую секунду.
Если внимательно присмотреться к взаимодействию по клику, вы увидите, что в результате события click вызываются две разные функции.
Как и ожидалось, первое обновление пользовательского интерфейса выполняется невероятно быстро, в то время как второе занимает целую секунду. Однако в совокупности их эффекты приводят к одинаково медленному взаимодействию с конечным пользователем.

Трассировка производительности: различные типы событий
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('pointerup', () => {
blockFor(1000);
});
Результаты очень похожи. Время взаимодействия по-прежнему составляет целую секунду; единственное отличие заключается в том, что более короткий обработчик click , выполняющийся только при обновлении пользовательского интерфейса, теперь запускается после блокирующего обработчика событий pointerup .

Трассировка производительности: обновление пользовательского интерфейса не выполнено.
Полный код смотрите здесь: no_ui.html
button.addEventListener('click', () => {
blockFor(1000);
// score.incrementAndUpdateUI();
});
- Счёт не обновляется, но страница по-прежнему отображается!
- Анимация, CSS-эффекты, действия веб-компонентов по умолчанию (ввод формы), ввод текста, выделение текста — все это продолжает обновляться.
В этом случае кнопка переходит в активное состояние и обратно при нажатии, что требует отрисовки браузером, а это значит, что по-прежнему присутствует ошибка INP.
Поскольку обработчик событий заблокировал основной поток на секунду, предотвратив отрисовку страницы, взаимодействие все равно занимает целую секунду.
Запись выступления участников дискуссии показывает, что их взаимодействие практически идентично тому, что происходило ранее.

Еда на вынос
Любой код, выполняющийся в любом обработчике событий, вызовет задержку взаимодействия.
- Это включает в себя обработчики событий, зарегистрированные из различных скриптов, а также код фреймворка или библиотеки, который выполняется в обработчиках, например, обновление состояния, запускающее рендеринг компонента.
- Это касается не только вашего собственного кода, но и всех сторонних скриптов.
Это распространённая проблема!
И наконец: тот факт, что ваш код не запускает отрисовку, не означает, что отрисовка не будет ожидать завершения медленных обработчиков событий.
7. Эксперимент: задержка входного сигнала
А что насчёт кода, выполняющегося длительное время вне обработчиков событий? Например:
- Если у вас был
<script>с задержкой загрузки, который случайным образом блокировал страницу во время загрузки. - Вызов API, например,
setInterval, который периодически блокирует страницу?
Попробуйте удалить метод blockFor из обработчика событий и добавить его в метод setInterval() :
Полный код смотрите здесь: input_delay.html
setInterval(() => {
blockFor(1000);
}, 3000);
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
Что происходит?
8. Результаты эксперимента по задержке входного сигнала
Полный код смотрите здесь: input_delay.html
setInterval(() => {
blockFor(1000);
}, 3000);
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
Запись нажатия кнопки, произошедшего во время выполнения блокирующей задачи setInterval , приводит к длительному взаимодействию, даже если в самом взаимодействии не выполняется никакой блокирующей работы!
Эти длительные периоды часто называют длительными задачами.
При наведении курсора на элемент взаимодействия в инструментах разработчика вы увидите, что время взаимодействия теперь в основном связано с задержкой ввода, а не с длительностью обработки.

Обратите внимание, это не всегда влияет на взаимодействие! Если вы не нажмете на кнопку во время выполнения задачи, вам может повезти. Такие «случайные» чихания могут стать настоящим кошмаром для отладки, если они лишь иногда вызывают проблемы.
Один из способов выявить их — измерить длительность задач (или длительность кадров анимации ) и общее время блокировки .
9. Медленная презентация
До сих пор мы рассматривали производительность JavaScript с помощью задержки ввода или обработчиков событий, но что еще влияет на отрисовку следующего элемента?
Что ж, обновляем страницу, добавляя дорогостоящие эффекты!
Даже если обновление страницы происходит быстро, браузеру всё равно, возможно, придётся приложить немало усилий для её отображения!
В основной ветке обсуждения:
- Фреймворки пользовательского интерфейса, которым необходимо отображать обновления после изменения состояния.
- Изменения в DOM или переключение множества ресурсоемких селекторов CSS-запросов могут вызвать множество операций стилизации, компоновки и отрисовки.
Вне основной ветки обсуждения:
- Использование CSS для управления эффектами графического процессора.
- Добавление очень больших изображений высокого разрешения
- Использование SVG/Canvas для рисования сложных сцен.

Вот несколько примеров, которые часто встречаются в интернете:
- SPA-сайт, который перестраивает весь DOM после перехода по ссылке, не делая пауз для отображения первоначальной визуальной обратной связи.
- Поисковая страница, предлагающая сложные фильтры поиска с динамическим пользовательским интерфейсом, но использующая для этого дорогостоящие обработчики событий.
- Переключатель темного режима, который изменяет стиль/макет всей страницы.
10. Эксперимент: задержка предъявления.
Медленный requestAnimationFrame
Давайте смоделируем длительную задержку отображения, используя API requestAnimationFrame() .
Переместите вызов blockFor в функцию обратного вызова requestAnimationFrame , чтобы он выполнялся после возврата обработчика события:
Полный код см. в файле presentation_delay.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
blockFor(1000);
});
});
Что происходит?
11. Результаты эксперимента с задержкой презентации.
Полный код см. в файле presentation_delay.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
blockFor(1000);
});
});
Взаимодействие длится всего секунду, так что же произошло?
requestAnimationFrame запрашивает обратный вызов перед следующей отрисовкой. Поскольку INP измеряет время от взаимодействия до следующей отрисовки, blockFor(1000) в requestAnimationFrame продолжает блокировать следующую отрисовку в течение целой секунды.

Однако обратите внимание на две вещи:
- При наведении курсора вы увидите, что все время взаимодействия теперь тратится на "задержку отображения", поскольку блокировка основного потока происходит после возврата обработчика событий.
- Корнем активности основного потока теперь является не событие клика, а событие "Animation Frame Fired".
12. Диагностика взаимодействий
На этой тестовой странице адаптивность очень заметна визуально: отображаются результаты, таймеры и счетчик... но при тестировании обычной страницы это проявляется более тонко.
Когда общение затягивается, не всегда ясно, в чём причина. Это:
- Задержка ввода?
- Продолжительность обработки события?
- Задержка презентации?
На любой странице вы можете использовать DevTools для оценки адаптивности. Чтобы выработать эту привычку, попробуйте следующий алгоритм:
- Пользуйтесь интернетом как обычно.
- Внимательно следите за журналом взаимодействий в режиме реального времени на панели «Производительность» в инструментах разработчика.
- Если вы заметили неэффективное взаимодействие, попробуйте его повторить:
- Если повторить это не удаётся, воспользуйтесь журналом взаимодействий, чтобы получить более подробную информацию.
- Если вам удастся воспроизвести проблему, запишите трассировку в панели «Производительность».
Все эти задержки
Попробуйте добавить на страницу понемногу от каждой из этих проблем:
Полный код смотрите здесь: all_the_things.html
setInterval(() => {
blockFor(1000);
}, 3000);
button.addEventListener('click', () => {
blockFor(1000);
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
blockFor(1000);
});
});
Затем используйте консоль и панель производительности для диагностики проблем!
13. Эксперимент: асинхронная работа
Поскольку невидимые эффекты можно запускать внутри интерактивных элементов, например, отправлять сетевые запросы, запускать таймеры или просто обновлять глобальное состояние, что произойдет, когда эти действия в конечном итоге обновят страницу?
Пока после взаимодействия выполняется отрисовка следующего изображения , даже если браузер решит, что новое обновление отрисовки на самом деле не требуется, измерение взаимодействия прекращается.
Чтобы это проверить, продолжайте обновлять пользовательский интерфейс из обработчика кликов, но выполняйте блокирующую работу с момента истечения таймаута.
Полный код: timeout_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
Что произойдет дальше?
14. Результаты эксперимента по асинхронной работе
Полный код: timeout_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});

Взаимодействие теперь кратковременное, поскольку основной поток становится доступен сразу после обновления пользовательского интерфейса. Длительная блокирующая задача по-прежнему выполняется, просто через некоторое время после отрисовки, поэтому пользователь получит немедленную обратную связь от интерфейса.
Урок: если это нельзя убрать, хотя бы переместите!
Методы
Можно ли добиться лучшего результата, чем фиксированное setTimeout в 100 миллисекунд? Вероятно, нам всё равно нужно, чтобы код выполнялся как можно быстрее, иначе нам следовало бы просто удалить это значение!
Цель:
- В результате взаимодействия будет запущена
incrementAndUpdateUI(). -
blockFor()выполнится как можно скорее, но не заблокирует следующую отрисовку. - Это приводит к предсказуемому поведению без "магических тайм-аутов".
Вот несколько способов достижения этой цели:
-
setTimeout(0) -
Promise.then() -
requestAnimationFrame -
requestIdleCallback -
scheduler.postTask()
"requestPostAnimationFrame"
В отличие от requestAnimationFrame самого по себе (который попытается выполниться до следующей отрисовки и, как правило, все равно будет замедлять взаимодействие), requestAnimationFrame + setTimeout представляет собой простой полифил для requestPostAnimationFrame , запускающий обратный вызов после следующей отрисовки.
Полный код смотрите здесь: raf+task.html
function afterNextPaint(callback) {
requestAnimationFrame(() => {
setTimeout(callback, 0);
});
}
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
afterNextPaint(() => {
blockFor(1000);
});
});
С точки зрения эргономики, это можно даже заключить в обещание:
Полный код смотрите здесь: raf+task2.html
async function nextPaint() {
return new Promise(resolve => afterNextPaint(resolve));
}
button.addEventListener('click', async () => {
score.incrementAndUpdateUI();
await nextPaint();
blockFor(1000);
});
15. Множественные взаимодействия (и клики в порыве гнева)
Перемещение длительных блокирующих задач может помочь, но эти задачи все равно блокируют страницу, влияя на будущие взаимодействия, а также на многие другие анимации и обновления страницы.
Попробуйте еще раз использовать версию страницы с асинхронной блокировкой выполнения задач (или свою собственную, если вы придумали свой вариант отложенной работы на предыдущем шаге):
Полный код: timeout_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
Что произойдет, если быстро несколько раз нажать на кнопку мыши?
трассировка производительности
Для каждого клика в очередь ставится задача длительностью в одну секунду, что обеспечивает блокировку основного потока на значительное время.

Когда эти длительные задачи перекрываются с новыми входящими кликами, это приводит к замедлению взаимодействия, даже несмотря на то, что обработчик событий возвращается почти мгновенно. Мы создали ту же ситуацию, что и в предыдущем эксперименте с задержками ввода. Только на этот раз задержка ввода возникает не из-за setInterval , а из-за работы, запущенной более ранними обработчиками событий.
Стратегии
В идеале мы хотим полностью исключить выполнение длительных задач!
- Удалите весь ненужный код, особенно скрипты.
- Оптимизируйте код, чтобы избежать выполнения длительных задач.
- Прекращайте выполнение устаревшей работы при появлении новых взаимодействий.
16. Стратегия 1: снижение частоты срабатывания.
Классическая стратегия. Всякий раз, когда взаимодействия происходят быстро, и обработка или сетевые эффекты требуют больших затрат, намеренно откладывайте начало работы, чтобы иметь возможность отменить и перезапустить её. Этот подход полезен для пользовательских интерфейсов, таких как поля автозаполнения.
- Используйте
setTimeout, чтобы отложить начало ресурсоемкой работы с помощью таймера, например, на 500–1000 миллисекунд. - При этом сохраните идентификатор таймера.
- Если поступает новое взаимодействие, отмените предыдущий таймер с помощью
clearTimeout.
Полный код смотрите здесь: debounce.html
let timer;
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
blockFor(1000);
}, 1000);
});
трассировка производительности

Несмотря на многочисленные клики, выполняется только одна задача blockFor , которая ждет, пока не пройдет целая секунда без кликов, прежде чем начать выполнение. Для взаимодействий, происходящих сериями — например, при вводе текста в поле ввода или при работе с элементами, которые, как ожидается, будут быстро кликаться несколько раз, — это идеальная стратегия для использования по умолчанию.
17. Стратегия 2: прерывание длительной работы
Всё ещё существует неприятная вероятность того, что после истечения периода подавления дребезга произойдёт ещё один щелчок, который застанет врасплох выполнение этой длительной задачи и приведёт к очень медленному взаимодействию из-за задержки ввода.
В идеале, если взаимодействие происходит в середине нашей задачи, мы хотим приостановить нашу рутинную работу , чтобы все новые взаимодействия обрабатывались немедленно. Как это можно сделать?
Существуют API-интерфейсы, например, isInputPending , но , как правило, лучше разбивать длинные задачи на части .
Много вызовов setTimeout
Первая попытка: сделайте что-нибудь простое.
Полный код смотрите здесь: small_tasks.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
});
});
Это работает за счет того, что браузер может планировать каждую задачу по отдельности, и ввод данных может иметь более высокий приоритет!

Мы снова работаем в режиме пяти секунд на пять кликов, но каждая односекундная задача на клик разбита на десять задач по 100 миллисекунд. В результате — даже при наличии нескольких взаимодействий, перекрывающихся с этими задачами, — ни одно взаимодействие не имеет задержки ввода более 100 миллисекунд! Браузер отдает приоритет входящим обработчикам событий перед работой setTimeout , и взаимодействия остаются отзывчивыми.
Эта стратегия особенно хорошо работает при планировании отдельных точек входа — например, если у вас есть множество независимых функций, которые необходимо вызывать во время загрузки приложения. Просто загрузка скриптов и запуск всего во время выполнения скрипта может по умолчанию привести к созданию огромной длительной задачи.
Однако эта стратегия не так хорошо работает для разделения тесно связанного кода, например, цикла for , использующего общее состояние.
Теперь с помощью yield()
Однако мы можем использовать современные async и оператор `async` и await , чтобы легко добавлять точки `yield` в любую функцию JavaScript.
Например:
Полный код смотрите здесь: yieldy.html
// Polyfill for scheduler.yield()
async function schedulerDotYield() {
return new Promise(resolve => {
setTimeout(resolve, 0);
});
}
async function blockInPiecesYieldy(ms) {
const ms_per_part = 10;
const parts = ms / ms_per_part;
for (let i = 0; i < parts; i++) {
await schedulerDotYield();
blockFor(ms_per_part);
}
}
button.addEventListener('click', async () => {
score.incrementAndUpdateUI();
await blockInPiecesYieldy(1000);
});
Как и прежде, основной поток освобождается после выполнения определенного объема работы, и браузер может реагировать на любые входящие взаимодействия, но теперь все, что требуется, — это await schedulerDotYield() вместо отдельных вызовов setTimeout , что делает его достаточно удобным для использования даже в середине цикла for .
Теперь с AbortContoller()
Это сработало, но каждое взаимодействие добавляет работы, даже если появились новые взаимодействия, которые могли изменить объем необходимой работы.
При использовании стратегии подавления дребезга мы отменяли предыдущий таймаут при каждом новом взаимодействии. Можно ли сделать что-то подобное здесь? Один из способов — использовать AbortController() :
Полный код см. в файле: aborty.html
// Polyfill for scheduler.yield()
async function schedulerDotYield() {
return new Promise(resolve => {
setTimeout(resolve, 0);
});
}
async function blockInPiecesYieldyAborty(ms, signal) {
const parts = ms / 10;
for (let i = 0; i < parts; i++) {
// If AbortController has been asked to stop, abandon the current loop.
if (signal.aborted) return;
await schedulerDotYield();
blockFor(10);
}
}
let abortController = new AbortController();
button.addEventListener('click', async () => {
score.incrementAndUpdateUI();
abortController.abort();
abortController = new AbortController();
await blockInPiecesYieldyAborty(1000, abortController.signal);
});
При щелчке мышью запускается цикл for blockInPiecesYieldyAborty , выполняющий необходимую работу и периодически освобождающий основной поток, чтобы браузер оставался отзывчивым к новым взаимодействиям.
При поступлении второго клика первый цикл помечается как отмененный с помощью AbortController , и запускается новый цикл blockInPiecesYieldyAborty — при следующем запланированном запуске первого цикла он обнаруживает, что signal.aborted теперь имеет true , и немедленно возвращается, не выполняя дальнейших действий.

18. Заключение
Разделение всех длительных задач позволяет сайту оперативно реагировать на новые взаимодействия. Это дает возможность быстро предоставлять первоначальную обратную связь, а также принимать решения, например, о прерывании текущей работы. Иногда это означает планирование точек входа в виде отдельных задач. Иногда это означает добавление точек «возврата» там, где это удобно.
Помнить
- INP измеряет все взаимодействия.
- Каждое взаимодействие измеряется от момента ввода до следующей отрисовки — так пользователь воспринимает скорость отклика.
- Задержка ввода, длительность обработки событий и задержка отображения — все эти факторы влияют на скорость реакции при взаимодействии.
- С помощью DevTools вы можете легко измерить INP и выявить сбои во взаимодействии!
Стратегии
- Не размещайте на своих страницах код, выполняющийся длительное время (длительные задачи).
- Вынесите ненужный код из обработчиков событий до следующей отрисовки.
- Убедитесь, что само обновление отрисовки эффективно для браузера.