Перейти к содержанию

Отклик интерфейса и Interaction to Next Paint

Основное содержание уже показано, изображения заняли свои места, но поле фильтра может реагировать медленно. Это другой этап пользовательского опыта. В этом уроке разберём Interaction to Next Paint, или INP, и научимся отделять ожидание обработки события от выполнения программы и следующего показанного кадра.

Работаем с карточками speed-lab. Набор из 2500 записей и дополнительная вычислительная сумма введены искусственно, чтобы исследовать основной поток. Они не описывают поиск ProfessorWeb. Автор не выполнял этот пример и не получил реальный INP; числовая декомпозиция ниже полностью учебная. Практический результат — собственная запись конкретного взаимодействия с объяснением задержки.

Три участка одной реакции

Человек нажал клавишу, но обработчик может начать работу позднее, если основной поток занят. Затем программа выполняет свои операции. После её завершения браузеру ещё нужно подготовить и показать кадр. Поэтому время самой функции фильтра не охватывает весь отклик. Пользователь воспринимает границы действия и видимого результата, а не только вход и выход JavaScript.

INP исследует реакции на подходящие взаимодействия в течение посещения. К ним относятся нажатия, касания и клавиши; обычная прокрутка сама по себе не является взаимодействием для расчёта INP. Метрика описывает выбранную медленную реакцию посещения с правилами обработки большого числа действий, поэтому это не среднее всех обработчиков. Хороший ориентир — не более 200 миллисекунд на 75-м процентиле группы посещений. Определение INP.

Отдельная трасса фильтра позволяет изучить механизм, но не определяет полевое распределение. Если вы ввели только одну тему на мощном компьютере, нельзя переносить результат на длинное посещение с другими действиями. Сохраняйте сценарий: когда начался ввод, как быстро менялись запросы, успела ли завершиться начальная обработка и использовалось ли замедление CPU.

Рассмотрим выдуманную учебную реакцию: ожидание обработчика 120 миллисекунд, работа 80, подготовка следующего кадра 40. Условная сумма равна 240 миллисекундам. Если оптимизировать только 80 миллисекунд функции, два остальных участка останутся. Эта арифметика не получена из стенда; она показывает, почему длительность функции недостаточна для объяснения всего отклика.

Синхронная обработка карточек

Baseline читает текущий запрос и проходит весь набор. Общая вычислительная сумма специально добавляет искусственную нагрузку; она выводится для сравнения результатов обоих алгоритмов. Код обработки приведён целиком, а records, checksum и render импортируются из общего модуля стенда:

function filter() {
  const query = input.value.trim().toLocaleLowerCase("ru");
  let value = 0;
  const matches = [];
  for (const record of records) {
    value = (value + checksum(record.title)) >>> 0;
    if (record.title.toLocaleLowerCase("ru").includes(query)) matches.push(record);
  }
  render(matches, value);
}
input.addEventListener("input", filter);

Для темы Markdown ожидается 500 совпадений, а для отсутствующей темы — ноль. Показаны только первые 30 карточек, поэтому вывод остаётся компактным. Эти результаты вытекают из подготовленного набора и должны быть вручную проверены читателем. Время зависит от устройства; одинаковое число записей не создаёт одинаковую задержку во всех окружениях.

В настоящем приложении сначала стоит спросить, нужна ли вычислительная работа вообще. Наш checksum искусственный: его удаление упростило бы учебную функцию, но скрыло бы демонстрируемый механизм. Производственный код не должен сохранять бессмысленную нагрузку ради похожести на пример. Позже можно рассмотреть порционную обработку для действительно необходимой операции.

Запись взаимодействия

Откройте Performance, начните запись и введите Markdown, затем быстро измените запрос на CSS. Найдите взаимодействия и связанные задачи Main. Сопоставьте начало действия, начало обработчика и следующий кадр. Не ограничивайтесь самой широкой функцией: большой интервал до неё может объяснять задержку ввода, а после — стоимость размещения карточек.

Если одновременно выполняется начальная обработка фильтра, ранний ввод попадает в другое условие, чем ввод после её окончания. Это полезно как отдельный сценарий, но его нужно записать. Страница может хорошо реагировать после прогрева и хуже во время старта. Называйте оба состояния, вместо того чтобы выбирать только лучший результат. Исследование причин INP.

Для измерения участка собственного кода можно временно использовать User Timing. Следующий диагностический фрагмент ставится непосредственно вокруг вызова и удаляется после изучения; он не заменяет INP:

performance.mark("filter-start");
filter();
performance.mark("filter-end");
performance.measure("filter-work", "filter-start", "filter-end");

Запись покажет границы синхронной функции. Она не включает очередь до начала вашего фрагмента и не гарантирует, что конечный кадр уже был представлен. Если перенести функцию в асинхронный вариант, границы также придётся переосмыслить. Измерение должно относиться к конкретной операции, иначе название filter-work создаст ложное ощущение полной реакции интерфейса.

Ранняя обратная связь

Интерфейс может сначала показать состояние «Обновляем карточки…», затем выполнить вычисление. Но изменение textContent не означает немедленный показ пикселей: если следом начинается долгий синхронный цикл, браузер может не получить возможность отобразить промежуточное состояние. Нужен подходящий способ передать управление, а не только строка с новым текстом.

Раннее сообщение также не заменяет завершение задачи. Пользователь должен получить актуальные карточки и понятное окончание ожидания. Если новое значение поля вводится быстрее, чем завершается старый расчёт, запоздалый результат способен перезаписать текущий. Тогда реакция кажется быстрой, но смысл ответа неверен. В следующем уроке введём порции и проверку ревизии запроса.

Не блокируйте поле автоматически, чтобы сократить число событий. Человек должен иметь возможность исправить запрос, особенно при длинном ожидании. Можно выбрать подходящий способ обработки частого ввода, но его ограничения должны быть понятны и не ухудшать клавиатурное взаимодействие. Доступность поля обсуждается в курсе доступности, здесь сохраняем рабочую функцию во время оптимизации.

Завершение посещения и область метрики

Не превращайте самое медленное замеченное нажатие в готовую полевую оценку сайта. Оно относится к вашей последовательности действий, пока не определены правила сбора и агрегации посещений. Для диагностической записи достаточно назвать событие и показать причинные участки. Позднее полноценный сборщик метрик сможет обрабатывать жизненный цикл и допустимые взаимодействия, но его нельзя заменить одной строкой performance.measure.

У интерфейса может быть несколько результатов одного действия. Поле быстро отражает введённую букву, сообщение сообщает о работе, а карточки появляются позже. Сравните, какую границу показывает инструмент и какая нужна пользователю для завершения задачи. Ранняя обратная связь полезна, однако она не означает немедленной готовности всего вычисления. Два состояния должны быть различимы и в тексте, и в интерфейсе.

Когда фильтр применяется при большом увеличении страницы, меняется также стоимость размещения результата. Поэтому одинаковый JavaScript может сопровождаться другой длительностью участка после обработчика. Проверьте эту гипотезу отдельно от изменения алгоритма. Если причина в количестве и оформлении показанных карточек, потребуется решение о представлении результата, сохранив доступ к нужным материалам.

К завершению сохраните одну запись реакции и распределите задержку по трём смысловым участкам. Для каждого участка предложите своё направление изменения. Если основная цена — размещение большого результата, деление вычисления не решит её целиком. Если очередь возникает из другой задачи, исправление фильтра может оказаться недостаточным. Такой вывод подготовит осмысленную работу с JavaScript, а не обещание ускорить любой обработчик одной конструкцией.

Оглавление курса