Измерение взаимодействия со следующей покраской (INP)

1. Введение

Это интерактивный практический курс, посвященный изучению измерения взаимодействия с объектом NextPaint (INP) с помощью библиотеки web-vitals .

Предварительные требования

Что вы узнаете

  • Как добавить библиотеку web-vitals на вашу страницу и использовать её данные для атрибуции.
  • Используйте данные об атрибуции, чтобы определить, где и как начать улучшать INP.

Что вам понадобится

  • Компьютер, способный клонировать код с GitHub и запускать команды npm.
  • Текстовый редактор.
  • Для корректной работы всех функций измерения взаимодействия необходима последняя версия Chrome.

2. Настройка

Получите и запустите код.

Код находится в репозитории web-vitals-codelabs .

  1. Клонируйте репозиторий в терминале: git clone https://github.com/GoogleChromeLabs/web-vitals-codelabs.git .
  2. Перейдите в клонированную директорию: cd web-vitals-codelabs/measuring-inp .
  3. Установите зависимости: npm ci .
  4. Запустите веб-сервер: npm run start .
  5. Откройте в браузере адрес http://localhost:8080/ .

Попробуйте эту страницу.

В этом практическом занятии используется Gastropodicon (популярный справочный сайт по анатомии улиток) для изучения потенциальных проблем, связанных с INP.

Скриншот демонстрационной страницы Gastropodicon.

Попробуйте взаимодействовать со страницей, чтобы понять, какие действия выполняются медленно.

3. Освоение инструментов разработчика Chrome

Откройте инструменты разработчика из меню «Дополнительные инструменты» > «Инструменты разработчика» , щелкнув правой кнопкой мыши на странице и выбрав «Проверить элемент» , или с помощью сочетания клавиш .

В этом практическом занятии мы будем использовать как панель «Производительность» , так и консоль . Вы можете переключаться между ними в любое время на вкладках в верхней части инструментов разработчика.

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

Скриншот панели «Производительность» в инструментах разработчика рядом с приложением, с выбранным параметром «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>

Обновите страницу, и теперь информация о взаимодействиях должна отображаться в консоли, обновляясь при появлении нового самого медленного взаимодействия. Например, попробуйте ввести текст в поле поиска, а затем удалить поле ввода.

Скриншот консоли DevTools с успешно выведенными в неё сообщениями INP.

5. Что подразумевается под атрибуцией?

Начнём с самого первого взаимодействия большинства пользователей со страницей — диалогового окна согласия на использование файлов cookie.

На многих страницах используются скрипты, для запуска которых синхронно требуются файлы cookie, когда пользователь их принимает, что приводит к замедлению взаимодействия при клике. Именно это и происходит здесь.

Нажмите «Да» , чтобы принять (демо) файлы cookie, и посмотрите на данные INP, которые теперь записываются в консоль DevTools.

Объект данных INP выведен в консоль инструментов разработчика.

Эта основная информация доступна как в стандартной сборке, так и в сборке 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 — затраченного на выполнение этого обработчика событий, фактически было потрачено на перерисовку страницы.

Это должно быть первоочередной задачей.

Повторные слушатели