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

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

4. Установка web-vitals
web-vitals — это библиотека JavaScript для измерения показателей Web Vitals, с которыми сталкиваются ваши пользователи. Вы можете использовать библиотеку для сбора этих значений, а затем передавать их на конечную точку аналитики для последующего анализа, например, для определения того, когда и где происходят медленные взаимодействия.
Существует несколько способов добавить библиотеку на страницу . Способ установки библиотеки на вашем собственном сайте будет зависеть от того, как вы управляете зависимостями, процессом сборки и другими факторами. Обязательно ознакомьтесь с документацией библиотеки, чтобы узнать обо всех доступных вариантах.
В этом практическом задании скрипт будет установлен из npm и загружен напрямую, чтобы избежать необходимости проходить отдельный процесс сборки.
Вы можете использовать две версии web-vitals :
- Для отслеживания значений метрик Core Web Vitals при загрузке страницы следует использовать "стандартную" сборку.
- Сборка с "атрибуцией" добавляет дополнительную отладочную информацию к каждой метрике, чтобы диагностировать, почему метрика в итоге получает именно такое значение.
Для измерения INP в этом практическом задании нам нужна сборка с указанием авторства.
Добавьте web-vitals в зависимости проекта devDependencies , выполнив команду npm install -D web-vitals
Добавьте на страницу атрибут web-vitals :
Добавьте версию скрипта с указанием авторства в конец файла index.html и выведите результаты в консоль:
<script type="module">
import {onINP} from './node_modules/web-vitals/dist/web-vitals.attribution.js';
onINP(console.log);
</script>
Попробуйте!
Попробуйте еще раз взаимодействовать со страницей, открыв консоль. При щелчках мышью по странице ничего не записывается в лог!
Показатель INP измеряется на протяжении всего жизненного цикла страницы, поэтому по умолчанию web-vitals не сообщает о показателе INP до тех пор, пока пользователь не покинет или не закроет страницу. Это идеальное поведение для использования маячков, например, в аналитике, но менее удобно для интерактивной отладки.
web-vitals предоставляет опцию reportAllChanges для более подробного отображения изменений . При включении этой опции сообщается не о каждом взаимодействии, но каждый случай, когда взаимодействие происходит медленнее, чем любое предыдущее, будет зафиксирован.
Попробуйте добавить эту опцию в скрипт и снова взаимодействовать со страницей:
<script type="module">
import {onINP} from './node_modules/web-vitals/dist/web-vitals.attribution.js';
onINP(console.log, {reportAllChanges: true});
</script>
Обновите страницу, и теперь информация о взаимодействиях должна отображаться в консоли, обновляясь при появлении нового самого медленного взаимодействия. Например, попробуйте ввести текст в поле поиска, а затем удалить поле ввода.

5. Что подразумевается под атрибуцией?
Начнём с самого первого взаимодействия большинства пользователей со страницей — диалогового окна согласия на использование файлов cookie.
На многих страницах используются скрипты, для запуска которых синхронно требуются файлы cookie, когда пользователь их принимает, что приводит к замедлению взаимодействия при клике. Именно это и происходит здесь.
Нажмите «Да» , чтобы принять (демо) файлы cookie, и посмотрите на данные INP, которые теперь записываются в консоль DevTools.

Эта основная информация доступна как в стандартной сборке, так и в сборке web-vitals с функцией атрибуции:
{
name: 'INP',
value: 344,
rating: 'needs-improvement',
entries: [...],
id: 'v4-1715732159298-8028729544485',
navigationType: 'reload',
attribution: {...},
}
Продолжительность времени от момента щелчка пользователя до следующего кадрирования составила 344 миллисекунды — это показатель, требующий улучшения . Массив entries содержит все значения PerformanceEntry , связанные с этим взаимодействием — в данном случае, только с одним событием щелчка.
Однако, чтобы выяснить, что происходит в это время, нас больше всего интересует свойство attribution . Для построения данных атрибуции web-vitals определяет, какой кадр длинной анимации (LoAF) совпадает с событием клика. Затем LoAF может предоставить подробные данные о том, сколько времени было потрачено в течение этого кадра, от запущенных скриптов до времени, затраченного на обратный вызов requestAnimationFrame , стиля и макета.
Разверните свойство attribution , чтобы увидеть более подробную информацию. Данные стали намного полнее.
attribution: {
interactionTargetElement: Element,
interactionTarget: '#confirm',
interactionType: 'pointer',
inputDelay: 27,
processingDuration: 295.6,
presentationDelay: 21.4,
processedEventEntries: [...],
longAnimationFrameEntries: [...],
}
Во-первых, есть информация о том, с чем именно вступало взаимодействие:
-
interactionTargetElement: текущая ссылка на элемент, с которым было произведено взаимодействие (если элемент не был удален из DOM). -
interactionTarget: селектор для поиска элемента на странице.
Далее, временные рамки рассматриваются в общих чертах:
-
inputDelay: время между моментом, когда пользователь начал взаимодействие (например, щелкнул мышью), и моментом, когда начал выполняться обработчик событий для этого взаимодействия. В данном случае задержка ввода составила всего около 27 миллисекунд, даже при включенном ограничении производительности процессора. -
processingDuration: время, необходимое для завершения работы обработчиков событий. Часто страницы имеют несколько обработчиков для одного события (например,pointerdown,pointerupиclick). Если все они выполняются в одном кадре анимации, они объединяются в это время. В этом случае processingDuration составляет 295,6 миллисекунд — основную часть времени INP. -
presentationDelay: время от завершения обработки событий обработчиками до завершения отрисовки следующего кадра браузером. В данном случае — 21,4 миллисекунды.
Эти фазы INP могут служить важным сигналом для диагностики того, что нуждается в оптимизации. В руководстве по оптимизации INP содержится дополнительная информация по этой теме .
Если копнуть глубже, то массив processedEventEntries содержит пять событий, в отличие от одного события в массиве entries верхнего уровня INP. В чём разница?
processedEventEntries: [
{
name: 'mouseover',
entryType: 'event',
startTime: 1801.6,
duration: 344,
processingStart: 1825.3,
processingEnd: 1825.3,
cancelable: true
},
{
name: 'mousedown',
entryType: 'event',
startTime: 1801.6,
duration: 344,
processingStart: 1825.3,
processingEnd: 1825.3,
cancelable: true
},
{name: 'mousedown', ...},
{name: 'mouseup', ...},
{name: 'click', ...},
],
Запись верхнего уровня — это событие INP, в данном случае клик. В поле ` processedEventEntries содержатся все события, обработанные в течение одного кадра. Обратите внимание, что оно включает и другие события, такие как mouseover и mousedown , а не только событие клика. Знание об этих других событиях может быть крайне важным, если они также замедляли работу, поскольку все они способствовали замедлению отклика.
Наконец, есть массив longAnimationFrameEntries . Он может содержать всего одну запись, но бывают случаи, когда взаимодействие может распространяться на несколько кадров. Здесь мы рассматриваем простейший случай с одним длинным кадром анимации.
longAnimationFrameEntries
Расширение записи LoAF:
longAnimationFrameEntries: [{
name: 'long-animation-frame',
startTime: 1823,
duration: 319,
renderStart: 2139.5,
styleAndLayoutStart: 2139.7,
firstUIEventTimestamp: 1801.6,
blockingDuration: 268,
scripts: [{...}]
}],
Здесь есть ряд полезных значений, например, указание времени, затраченного на стилизацию. В статье об API длительных анимационных кадров более подробно рассматриваются эти свойства . Сейчас нас в первую очередь интересует свойство scripts , которое содержит записи, предоставляющие подробную информацию о скриптах, отвечающих за длительный анимационный кадр:
scripts: [{
name: 'script',
invoker: 'BUTTON#confirm.onclick',
invokerType: 'event-listener',
startTime: 1828.6,
executionStart: 1828.6,
duration: 294,
sourceURL: 'http://localhost:8080/third-party/cmp.js',
sourceFunctionName: '',
sourceCharPosition: 1144
}]
В данном случае мы можем сказать, что основное время было потрачено на event-listener , вызываемого при нажатии BUTTON#confirm.onclick . Мы даже можем увидеть URL-адрес исходного кода скрипта и позицию символа, где была определена функция!
Еда на вынос
Что можно заключить об этом случае на основании данных об атрибуции?
- Взаимодействие запускалось щелчком по элементу
button#confirm(изattribution.interactionTargetи свойстваinvokerзаписи об атрибуции скрипта). - В основном время уходило на выполнение обработчиков событий (на основе
attribution.processingDurationпо сравнению с общимvalueметрики). - Код для обработки медленных событий начинается с обработчика кликов, определенного в файле
third-party/cmp.js(изscripts.sourceURL).
Этого количества данных достаточно, чтобы понять, где нам нужно оптимизировать процесс!
6. Множественные обработчики событий
Обновите страницу, чтобы консоль инструментов разработчика стала чистой, а взаимодействие с запросом согласия на использование файлов cookie перестало быть самым длительным.
Начните вводить текст в поле поиска. Что показывают данные атрибуции? Как вы думаете, что происходит?
Данные об атрибуции
Для начала, краткий обзор одного из примеров тестирования демоверсии:
{
name: 'INP',
value: 1072,
rating: 'poor',
attribution: {
interactionTargetElement: Element,
interactionTarget: '#search-terms',
interactionType: 'keyboard',
inputDelay: 3.3,
processingDuration: 1060.6,
presentationDelay: 8.1,
processedEventEntries: [...],
longAnimationFrameEntries: [...],
}
}
Это низкое значение INP (с включенным ограничением производительности ЦП) из-за взаимодействия с клавиатурой элемента input#search-terms . Большая часть времени — 1061 миллисекунда из 1072 миллисекунд общего времени INP — была затрачена на обработку.
Однако записи scripts представляют больший интерес.
Разборка макета
Первая запись в массиве scripts предоставляет нам ценную информацию:
scripts: [{
name: 'script',
invoker: 'BUTTON#confirm.onclick',
invokerType: 'event-listener',
startTime: 4875.6,
executionStart: 4875.6,
duration: 497,
forcedStyleAndLayoutDuration: 388,
sourceURL: 'http://localhost:8080/js/index.js',
sourceFunctionName: 'handleSearch',
sourceCharPosition: 940
},
...]
Большая часть времени обработки приходится на выполнение этого скрипта, который является обработчиком input (вызывающий объект — INPUT#search-terms.oninput ). Указывается имя функции ( handleSearch ), а также позиция символа в исходном файле index.js .
Однако появилось новое свойство: forcedStyleAndLayoutDuration . Это время, затраченное браузером на принудительную перерисовку страницы в рамках выполнения данного скрипта. Другими словами, 78% времени — 388 миллисекунд из 497 — затраченного на выполнение этого обработчика событий, фактически было потрачено на перерисовку страницы.
Это должно быть первоочередной задачей.