Узнайте, как находить медленные взаимодействия в полевых данных вашего веб-сайта, чтобы вы могли найти возможности для улучшения взаимодействия с ним до следующей отрисовки.
Данные полевых испытаний — это данные, которые показывают, как пользователи взаимодействуют с вашим сайтом. Они выявляют проблемы, которые невозможно обнаружить только с помощью лабораторных данных . Что касается взаимодействия до следующей отрисовки (INP) , данные полевых испытаний играют ключевую роль в выявлении медленных взаимодействий и предоставляют важные подсказки для их устранения.
В этом руководстве вы узнаете, как быстро оценить INP вашего сайта, используя данные полей из отчёта об удобстве использования Chrome (CrUX), чтобы определить, есть ли у вашего сайта проблемы с INP. Далее вы узнаете, как использовать атрибуционную сборку библиотеки JavaScript web-vitals и новые аналитические данные, предоставляемые API длинных кадров анимации (LoAF), для сбора и интерпретации данных полей для медленного взаимодействия с вашим сайтом.
Начните с CrUX, чтобы оценить INP вашего сайта
Если вы не собираете полевые данные от пользователей своего сайта, CrUX может стать хорошей отправной точкой. CrUX собирает полевые данные от реальных пользователей Chrome, которые согласились отправлять телеметрические данные.
Данные CrUX доступны в различных областях, в зависимости от объема информации, которую вы ищете. CrUX может предоставить данные по INP и другим основным веб-показателям для:
- Отдельные страницы и целые источники с использованием PageSpeed Insights .
- Типы страниц. Например, многие сайты электронной коммерции имеют типы страниц «Страница с описанием товара» и «Страница со списком товаров». Вы можете получить данные CrUX для уникальных типов страниц в Search Console .
Для начала вы можете ввести URL-адрес своего сайта в PageSpeed Insights. После ввода URL-адреса данные полей для него (если они доступны) будут отображаться для нескольких показателей, включая INP. Вы также можете использовать переключатели для проверки значений INP для мобильных устройств и компьютеров.

Эти данные полезны, поскольку сообщают о наличии проблем. Однако CrUX не может определить, что именно их вызывает. Существует множество решений для мониторинга реальных пользователей (RUM), которые помогут вам собрать собственные данные о пользователях вашего сайта, чтобы помочь вам решить эту проблему. Один из вариантов — собрать эти данные самостоятельно с помощью JavaScript-библиотеки web-vitals.
Собирайте полевые данные с помощью библиотеки JavaScript web-vitals
Библиотека JavaScript web-vitals
— это скрипт, который вы можете загрузить на свой сайт для сбора полевых данных пользователей. Вы можете использовать его для регистрации ряда показателей, включая INP, в браузерах, которые его поддерживают.
Стандартную сборку библиотеки web-vitals можно использовать для получения основных данных INP от пользователей на местах:
import {onINP} from 'web-vitals';
onINP(({name, value, rating}) => {
console.log(name); // 'INP'
console.log(value); // 512
console.log(rating); // 'poor'
});
Чтобы проанализировать полевые данные ваших пользователей, вам нужно отправить эти данные куда-то:
import {onINP} from 'web-vitals';
onINP(({name, value, rating}) => {
// Prepare JSON to be sent for collection. Note that
// you can add anything else you'd want to collect here:
const body = JSON.stringify({name, value, rating});
// Use `sendBeacon` to send data to an analytics endpoint.
// For Google Analytics, see https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics.
navigator.sendBeacon('/analytics', body);
});
Однако сами по себе эти данные не дают вам гораздо больше информации, чем CrUX. Именно здесь вступает в дело атрибуция библиотеки web-vitals.
Расширьте возможности библиотеки Web-Vitals с помощью атрибуции
Атрибутивная сборка библиотеки web-vitals предоставляет дополнительные данные, которые вы можете получить от пользователей на местах, чтобы помочь вам эффективнее устранять неполадки, связанные с взаимодействием, влияющими на INP вашего веб-сайта. Эти данные доступны через объект attribution
отображаемый в методе onINP()
библиотеки:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, rating, attribution}) => {
console.log(name); // 'INP'
console.log(value); // 56
console.log(rating); // 'good'
console.log(attribution); // Attribution data object
});

Помимо самого INP страницы, атрибуционная сборка предоставляет множество данных, которые помогут понять причины медленного взаимодействия, в том числе определить, на какой части взаимодействия следует сосредоточиться. Она может помочь вам ответить на важные вопросы, такие как:
- «Взаимодействовал ли пользователь со страницей во время ее загрузки?»
- «Долго ли работали обработчики событий взаимодействия?»
- «Был ли отложен запуск кода обработчика событий взаимодействия? Если да, то что ещё происходило в основном потоке в это время?»
- «Вызвало ли это взаимодействие большой объем работы по рендерингу, из-за которого отрисовка следующего кадра задержалась?»
В следующей таблице показаны некоторые основные данные атрибуции, которые вы можете получить из библиотеки и которые помогут вам выяснить некоторые общие причины медленного взаимодействия на вашем веб-сайте:
ключ объекта attribution | Данные |
---|---|
interactionTarget | Селектор CSS, указывающий на элемент, который создал значение INP страницы, например, button#save . |
interactionType | Тип взаимодействия: щелчки, нажатия или ввод с клавиатуры. |
inputDelay * | Задержка ввода взаимодействия. |
processingDuration * | Время с момента запуска первого прослушивателя событий в ответ на взаимодействие с пользователем до момента завершения всей обработки прослушивателя событий. |
presentationDelay * | Задержка представления взаимодействия, которая начинается с момента завершения работы обработчиков событий и заканчивается моментом отрисовки следующего кадра. |
longAnimationFrameEntries * | Записи из LoAF, связанные с этим взаимодействием. Дополнительную информацию см. далее. |
Начиная с версии 4 библиотеки web-vitals, вы можете получить еще более глубокое представление о проблемных взаимодействиях с помощью данных, которые она предоставляет с разбивкой фаз INP (задержка ввода, длительность обработки и задержка представления) и API длинных кадров анимации (LoAF) .
API длинных кадров анимации (LoAF)
Отладка взаимодействий с использованием полевых данных — непростая задача. Однако благодаря данным LoAF теперь можно лучше понять причины медленного взаимодействия, поскольку LoAF предоставляет ряд подробных временных показателей и других данных, которые можно использовать для точного определения причин и, что ещё важнее, для определения источника проблемы в коде вашего сайта.
Сборка атрибуции библиотеки web-vitals предоставляет массив записей LoAF по ключу longAnimationFrameEntries
объекта attribution
. В следующей таблице перечислены некоторые ключевые фрагменты данных, которые можно найти в каждой записи LoAF:
Ключ объекта записи LoAF | Данные |
---|---|
duration | Длительность длинного кадра анимации до момента завершения макета, но без учета отрисовки и компоновки. |
blockingDuration | Общее время в кадре, в течение которого браузер не мог быстро отреагировать из-за длительных задач. Это время блокировки может включать в себя длительные задачи, выполняющие JavaScript, а также любые последующие длительные задачи рендеринга в кадре. |
firstUIEventTimestamp | Временная метка, указывающая, когда событие было поставлено в очередь в течение кадра. Полезно для определения начала задержки ввода взаимодействия. |
startTime | Начальная временная метка кадра. |
renderStart | Когда началась работа по рендерингу кадра. Это включает в себя все обратные вызовы requestAnimationFrame (и обратные вызовы ResizeObserver , если применимо), но, возможно, до начала работы над стилями/макетом. |
styleAndLayoutStart | Когда в кадре выполняется работа по стилю/вёрстке. Может быть полезно для определения продолжительности работы по стилю/вёрстке при учёте других доступных временных меток. |
scripts | Массив элементов, содержащих информацию об атрибуции скрипта, влияющую на INP страницы. |

blockingDuration
). Вся эта информация может многое рассказать о причинах замедления взаимодействия, но массив scripts
, который выводят записи LoAF, должен представлять особый интерес:
Ключ объекта атрибуции скрипта | Данные |
---|---|
invoker | Вызывающий объект. Может различаться в зависимости от типа вызывающего объекта, описанного в следующей строке. Примерами вызывающих объектов могут быть такие значения, как 'IMG#id.onload' , 'Window.requestAnimationFrame' или 'Response.json.then' . |
invokerType | Тип вызывающего объекта. Может быть: 'user-callback' , 'event-listener' , 'resolve-promise' , 'reject-promise' , 'classic-script' или 'module-script' . |
sourceURL | URL-адрес скрипта, из которого был взят длинный кадр анимации. |
sourceCharPosition | Позиция символа в сценарии, определяемая sourceURL . |
sourceFunctionName | Имя функции в идентифицированном скрипте. |
Каждая запись в этом массиве содержит данные, показанные в этой таблице, которая дает вам информацию о скрипте, ответственном за медленное взаимодействие, и о том, как именно он был ответственен.
Измерьте и определите общие причины медленного взаимодействия
Чтобы дать вам представление о том, как можно использовать эту информацию, в этом руководстве будет подробно рассмотрено, как можно использовать данные LoAF, полученные в библиотеке web-vitals
для определения некоторых причин медленного взаимодействия.
Длительная продолжительность обработки
Длительность обработки взаимодействия — это время, необходимое для завершения выполнения зарегистрированных обработчиков событий взаимодействия, а также любые другие события, которые могут произойти между ними. Библиотека web-vitals выявляет высокую длительность обработки:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {processingDuration} = attribution; // 512.5
});
Естественно предположить, что основная причина медленного взаимодействия — слишком долгое выполнение кода обработчика событий, но это не всегда так! Убедившись, что проблема именно в этом, можно изучить данные LoAF более подробно:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {processingDuration} = attribution; // 512.5
// Get the longest script from LoAF covering `processingDuration`:
const loaf = attribution.longAnimationFrameEntries.at(-1);
const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];
if (script) {
// Get attribution for the long-running event handler:
const {invokerType} = script; // 'event-listener'
const {invoker} = script; // 'BUTTON#update.onclick'
const {sourceURL} = script; // 'https://example.com/app.js'
const {sourceCharPosition} = script; // 83
const {sourceFunctionName} = script; // 'update'
}
});
Как видно из предыдущего фрагмента кода, вы можете работать с данными LoAF, чтобы отследить точную причину взаимодействия с высокими значениями длительности обработки, включая:
- Элемент и его зарегистрированный прослушиватель событий.
- Файл скрипта (и позиция символа в нем), содержащий код обработчика долго выполняемых событий.
- Название функции.
Эти данные бесценны. Вам больше не нужно тратить время на выяснение того, какое именно взаимодействие или какие из его обработчиков событий ответственны за высокие значения длительности обработки. Кроме того, поскольку сторонние скрипты часто регистрируют собственные обработчики событий, вы можете определить, был ли причиной этого ваш код! Для кода, который вы контролируете, вам стоит рассмотреть возможность оптимизации длительных задач .
Длительные задержки ввода
Хотя обработчики событий с длительным временем выполнения широко распространены, существуют и другие аспекты взаимодействия, которые следует учитывать. Один из них происходит до начала обработки и называется задержкой ввода . Это время с момента, когда пользователь инициирует взаимодействие, до момента начала выполнения обратных вызовов обработчика событий, и происходит, когда основной поток уже обрабатывает другую задачу. Сборка атрибуции библиотеки web-vitals позволяет определить длительность задержки ввода для взаимодействия:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {inputDelay} = attribution; // 125.59439536
});
Если вы заметили, что некоторые взаимодействия имеют большие задержки ввода, то вам нужно выяснить, что происходило на странице во время взаимодействия и вызвало длительную задержку ввода, — а это часто сводится к тому, произошло ли взаимодействие во время загрузки страницы или после нее.
Это произошло во время загрузки страницы?
Основной поток часто наиболее загружен во время загрузки страницы. В это время всевозможные задачи ставятся в очередь и обрабатываются, и если пользователь попытается взаимодействовать со страницей во время всей этой работы, это может задержать взаимодействие. Страницы, загружающие большой объём JavaScript, могут инициировать компиляцию и оценку скриптов, а также выполнение функций, подготавливающих страницу к взаимодействию с пользователем. Эта работа может помешать, если пользователь взаимодействует с ней во время загрузки. Вы можете узнать, относится ли это к пользователям вашего сайта:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {inputDelay} = attribution; // 125.59439536
// Get the longest script from the first LoAF entry:
const loaf = attribution.longAnimationFrameEntries[0];
const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];
if (script) {
// Invoker types can describe if script eval blocked the main thread:
const {invokerType} = script; // 'classic-script' | 'module-script'
const {sourceLocation} = script; // 'https://example.com/app.js'
}
});
Если вы регистрируете эти данные в полевых условиях и видите высокие задержки ввода и типы вызова 'classic-script'
или 'module-script'
, то можно с уверенностью сказать, что скрипты на вашем сайте долго обрабатываются и блокируют основной поток достаточно долго, чтобы задержать взаимодействие. Вы можете сократить это время блокировки, разбив скрипты на более мелкие пакеты, отложив загрузку изначально неиспользуемого кода на более поздний момент и проведя аудит сайта на предмет неиспользуемого кода, который можно полностью удалить.
Это произошло после загрузки страницы?
Хотя задержки ввода часто возникают во время загрузки страницы, они также могут возникнуть и после её загрузки, по совершенно другой причине. Распространенными причинами задержек ввода после загрузки страницы могут быть код, который периодически запускается из-за более раннего вызова setInterval
, или даже обратные вызовы событий, поставленные в очередь на выполнение ранее и всё ещё обрабатываемые.
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {inputDelay} = attribution; // 125.59439536
// Get the longest script from the first LoAF entry:
const loaf = attribution.longAnimationFrameEntries[0];
const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];
if (script) {
const {invokerType} = script; // 'user-callback'
const {sourceURL} = script; // 'https://example.com/app.js'
const {sourceCharPosition} = script; // 83
const {sourceFunctionName} = script; // 'update'
}
});
Как и в случае с устранением неполадок, связанных с высокой длительностью обработки, высокие задержки ввода, вызванные упомянутыми ранее причинами, предоставят вам подробные данные об атрибуции скрипта. Однако отличие заключается в том, что тип инициатора будет меняться в зависимости от характера работы, вызвавшей задержку взаимодействия:
-
'user-callback'
указывает, что задача блокировки была выполнена методомsetInterval
,setTimeout
или дажеrequestAnimationFrame
. -
'event-listener'
указывает, что блокирующая задача была получена из более раннего ввода, который был поставлен в очередь и все еще обрабатывается. -
'resolve-promise'
и'reject-promise'
означают, что блокирующая задача была вызвана некоторой асинхронной работой, которая была запущена ранее и разрешена или отклонена в тот момент, когда пользователь пытался взаимодействовать со страницей, что привело к задержке взаимодействия.
В любом случае данные об атрибуции скрипта дадут вам представление о том, с чего начать поиск и была ли задержка ввода вызвана вашим собственным кодом или сторонним скриптом.
Длительные задержки презентаций
Задержки отображения — это последняя миля взаимодействия, которая начинается с момента завершения работы обработчиков событий взаимодействия и продолжается до момента отрисовки следующего кадра. Они возникают, когда работа обработчика событий, вызванная взаимодействием, изменяет визуальное состояние пользовательского интерфейса. Как и в случае с длительностью обработки и задержками ввода, библиотека web-vitals может подсказать длительность задержки отображения для взаимодействия:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {presentationDelay} = attribution; // 113.32307691
});
Если вы записываете эти данные и видите высокие задержки отображения взаимодействий, влияющих на INP вашего веб-сайта, виновники могут быть разными, но вот несколько причин, на которые следует обратить внимание.
Дорогая работа по стилю и планировке
Длительные задержки отображения могут привести к дорогостоящему перерасчёту стилей и верстке , что обусловлено рядом причин, включая сложные селекторы CSS и большие размеры DOM . Продолжительность этой работы можно измерить с помощью таймингов LoAF, представленных в библиотеке web-vitals:
import {onINP} from 'web-vitals/attribution';
onINP(({name, value, attribution}) => {
const {presentationDelay} = attribution; // 113.32307691
// Get the longest script from the last LoAF entry:
const loaf = attribution.longAnimationFrameEntries.at(-1);
const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];
// Get necessary timings:
const {startTime} = loaf; // 2120.5
const {duration} = loaf; // 1002
// Figure out the ending timestamp of the frame (approximate):
const endTime = startTime + duration; // 3122.5
// Get the start timestamp of the frame's style/layout work:
const {styleAndLayoutStart} = loaf; // 3011.17692309
// Calculate the total style/layout duration:
const styleLayoutDuration = endTime - styleAndLayoutStart; // 111.32307691
if (script) {
// Get attribution for the event handler that triggered
// the long-running style and layout operation:
const {invokerType} = script; // 'event-listener'
const {invoker} = script; // 'BUTTON#update.onclick'
const {sourceURL} = script; // 'https://example.com/app.js'
const {sourceCharPosition} = script; // 83
const {sourceFunctionName} = script; // 'update'
}
});
LoAF не сообщит вам длительность работы над стилем и макетом для кадра, но сообщит, когда она началась. С помощью этой начальной метки времени вы можете использовать другие данные LoAF для расчета точной длительности этой работы, определив время окончания кадра и вычтя из него начальную метку времени работы над стилем и макетом.
Длительные обратные вызовы requestAnimationFrame
Одной из возможных причин длительных задержек отображения является чрезмерная нагрузка на функцию обратного вызова requestAnimationFrame
. Содержимое этой функции обратного вызова выполняется после завершения работы обработчиков событий, но непосредственно перед пересчётом стилей и макетом.
Выполнение этих обратных вызовов может занять значительное время, если выполняемая ими работа сложна. Если вы подозреваете, что высокие значения задержки отображения связаны с работой с requestAnimationFrame
, вы можете использовать данные LoAF, предоставляемые библиотекой web-vitals, для выявления следующих сценариев:
onINP(({name, value, attribution}) => {
const {presentationDelay} = attribution; // 543.1999999880791
// Get the longest script from the last LoAF entry:
const loaf = attribution.longAnimationFrameEntries.at(-1);
const script = loaf?.scripts.toSorted((a, b) => b.duration - a.duration)[0];
// Get the render start time and when style and layout began:
const {renderStart} = loaf; // 2489
const {styleAndLayoutStart} = loaf; // 2989.5999999940395
// Calculate the `requestAnimationFrame` callback's duration:
const rafDuration = styleAndLayoutStart - renderStart; // 500.59999999403954
if (script) {
// Get attribution for the event handler that triggered
// the long-running requestAnimationFrame callback:
const {invokerType} = script; // 'user-callback'
const {invoker} = script; // 'FrameRequestCallback'
const {sourceURL} = script; // 'https://example.com/app.js'
const {sourceCharPosition} = script; // 83
const {sourceFunctionName} = script; // 'update'
}
});
Если вы видите, что значительная часть времени задержки отображения тратится на обратный вызов requestAnimationFrame
, убедитесь, что работа, выполняемая в этих обратных вызовах, ограничивается действиями, приводящими к фактическому обновлению пользовательского интерфейса. Любая другая работа, не затрагивающая DOM или обновление стилей, приведет к неоправданной задержке отрисовки следующего кадра, поэтому будьте осторожны!
Заключение
Данные полевых испытаний — лучший источник информации, на который можно опереться, когда речь идёт о понимании того, какие взаимодействия вызывают проблемы у реальных пользователей. Используя инструменты сбора полевых данных, такие как JavaScript-библиотека web-vitals (или поставщик RUM), вы сможете точнее определить, какие взаимодействия вызывают наибольшие проблемы, а затем воспроизвести проблемные взаимодействия в лабораторных условиях и приступить к их устранению.
Главное изображение из Unsplash , автор Федерико Респини .