API длинных кадров анимации (LoAF произносится как Lo-Af) — это обновление API длинных задач, позволяющее лучше понять медленные обновления пользовательского интерфейса (UI). Это может быть полезно для выявления кадров медленной анимации, которые могут повлиять на метрику Interaction to Next Paint (INP) Core Web Vital, которая измеряет скорость реагирования, или для выявления других ошибок пользовательского интерфейса, влияющих на плавность .
Статус API
После первоначального пробного перехода от Chrome 116 к Chrome 122 API LoAF был переведен из Chrome 123 .
Предыстория: API длинных задач
API Long Animation Frames — это альтернатива API Long Tasks, который уже некоторое время доступен в Chrome (начиная с Chrome 58). Как следует из названия, API длинных задач позволяет отслеживать длинные задачи, то есть задачи, которые занимают основной поток в течение 50 миллисекунд или дольше. Длинные задачи можно отслеживать с помощью интерфейса PerformanceLongTaskTiming
с помощью PeformanceObserver
:
const observer = new PerformanceObserver((list) => {
console.log(list.getEntries());
});
observer.observe({ type: 'longtask', buffered: true });
Длинные задачи могут вызвать проблемы с реагированием. Если пользователь пытается взаимодействовать со страницей (например, нажать кнопку или открыть меню), но основной поток уже выполняет длинную задачу, то взаимодействие пользователя задерживается в ожидании завершения этой задачи.
Чтобы улучшить скорость реагирования, часто советуют разбивать длинные задачи . Если вместо этого каждая длинная задача разбивается на серию нескольких более мелких задач, это может позволить выполнять более важные задачи между ними, чтобы избежать значительных задержек в реагировании на взаимодействия.
Поэтому при попытке улучшить скорость реагирования в первую очередь часто приходится запускать трассировку производительности и анализировать длинные задачи. Это можно сделать с помощью лабораторного инструмента аудита, такого как Lighthouse (который имеет функцию « Избегать длинных задач основного потока »), или путем просмотра длинных задач в Chrome DevTools .
Лабораторное тестирование часто является плохой отправной точкой для выявления проблем с реагированием , поскольку эти инструменты могут не учитывать взаимодействия, а если и включают, то представляют собой небольшую подгруппу вероятных взаимодействий. В идеале вы должны измерить причины медленного взаимодействия в полевых условиях.
Недостатки API длинных задач
Измерение длительных задач в полевых условиях с помощью Performance Observer приносит лишь некоторую пользу. На самом деле он не дает столько информации, кроме того факта, что была выполнена длинная задача и сколько времени она заняла.
Инструменты реального мониторинга пользователей (RUM) часто используют это для отслеживания количества или продолжительности длительных задач или определения того, на каких страницах они выполняются, но без основных подробностей о том, что вызвало длительную задачу, это имеет лишь ограниченное применение. API длинных задач имеет только базовую модель атрибуции , которая в лучшем случае сообщает вам только контейнер, в котором возникла длинная задача (документ верхнего уровня или <iframe>
), но не сценарий или функцию, вызвавшую ее, как показано на рисунке. типичная запись:
{
"name": "unknown",
"entryType": "longtask",
"startTime": 31.799999997019768,
"duration": 136,
"attribution": [
{
"name": "unknown",
"entryType": "taskattribution",
"startTime": 0,
"duration": 0,
"containerType": "window",
"containerSrc": "",
"containerId": "",
"containerName": ""
}
]
}
API длинных задач также является неполным представлением, поскольку также может исключать некоторые важные задачи. Некоторые обновления, такие как рендеринг, происходят в отдельных задачах, которые в идеале должны быть включены вместе с предыдущим выполнением, чтобы это обновление точно измеряло «общую работу» для этого взаимодействия. Более подробную информацию об ограничениях, связанных с использованием задач, см . в разделе пояснения «Когда не хватает длинных задач» .
Последняя проблема заключается в том, что измерение длительных задач сообщает только об отдельных задачах, длительность которых превышает лимит в 50 миллисекунд. Кадр анимации может состоять из нескольких задач, меньших по времени, чем этот предел в 50 миллисекунд, но в совокупности все равно блокировать возможность рендеринга браузера.
API длинных кадров анимации
API Long Animation Frames (LoAF) — это новый API, который призван устранить некоторые недостатки API Long Tasks, чтобы дать разработчикам возможность получить более полезную информацию, которая поможет решить проблемы с быстродействием и улучшить INP, а также получить представление о проблемах плавности. .
Хорошая отзывчивость означает, что страница быстро реагирует на взаимодействия с ней. Это предполагает возможность своевременно создавать любые обновления, необходимые пользователю, и избегать блокировки этих обновлений. Для INP рекомендуется отвечать за 200 миллисекунд или меньше , но для других обновлений (например, анимации) даже 200 миллисекунд могут оказаться слишком долгими.
API длинных кадров анимации — это альтернативный подход к измерению работы блокировки. Вместо измерения отдельных задач API Long Animation Frames, как следует из названия, измеряет длинные кадры анимации . Длинный кадр анимации — это когда обновление рендеринга задерживается более чем на 50 миллисекунд (так же, как пороговое значение для API длинных задач).
Длинные кадры анимации отсчитываются от начала задач, требующих рендеринга. Если первая задача в потенциально длинном кадре анимации не требует рендеринга, длинный кадр анимации завершается после завершения задачи, не связанной с рендерингом, и новый потенциальный длинный кадр анимации начинается со следующей задачи. Такие длинные кадры анимации без рендеринга по-прежнему включаются в API длинных кадров анимации, если их длительность превышает 50 миллисекунд (со временем renderStart
равным 0), чтобы можно было измерить потенциально блокирующую работу.
Длинные кадры анимации можно наблюдать так же, как и длинные задачи, с помощью PerformanceObserver
, но вместо этого обращая внимание на тип long-animation-frame
:
const observer = new PerformanceObserver((list) => {
console.log(list.getEntries());
});
observer.observe({ type: 'long-animation-frame', buffered: true });
Предыдущие длинные кадры анимации также можно запросить на временной шкале производительности следующим образом:
const loafs = performance.getEntriesByType('long-animation-frame');
Однако для записей производительности существует maxBufferSize
, после которого новые записи удаляются, поэтому рекомендуемым подходом является подход PerformanceObserver. Размер буфера long-animation-frame
равен 200, как и для long-tasks
.
Преимущества просмотра фреймов вместо задач
Ключевое преимущество рассмотрения этого с точки зрения кадра, а не с точки зрения задач, заключается в том, что длинная анимация может состоять из любого количества задач, которые в совокупности привели к созданию длинного кадра анимации. Это решает последний момент, упомянутый ранее, когда сумма многих более мелких задач, блокирующих рендеринг, перед кадром анимации может не отображаться API длинных задач.
Еще одним преимуществом этого альтернативного подхода к длительным задачам является возможность разбивки по времени всего кадра. Вместо того, чтобы просто включать startTime
и duration
, как API длинных задач, LoAF включает гораздо более подробную разбивку различных частей длительности кадра.
Временные метки и продолжительность кадров
-
startTime
: время начала длинного кадра анимации относительно времени начала навигации. -
duration
: продолжительность длинного кадра анимации (не включая время презентации). -
renderStart
: время начала цикла рендеринга, который включает в себя обратные вызовыrequestAnimationFrame
, расчет стиля и макета, обратные вызовы наблюдателя изменения размера и наблюдателя пересечения. -
styleAndLayoutStart
: начало периода времени, затраченного на расчеты стиля и макета. -
firstUIEventTimestamp
: время первого события пользовательского интерфейса (мыши/клавиатуры и т. д.), которое будет обработано в течение этого кадра. -
blockingDuration
: общая продолжительность в миллисекундах, в течение которой кадр анимации будет блокировать обработку ввода или другие задачи с высоким приоритетом.
Объяснение blockingDuration
Длинный кадр анимации может состоять из нескольких задач. blockingDuration
— это сумма длительности задачи, превышающей 50 миллисекунд (включая окончательную продолжительность рендеринга в самой длинной задаче).
Например, если длинный кадр анимации состоит из двух задач по 55 миллисекунд и 65 миллисекунд, за которыми следует рендеринг продолжительностью 20 миллисекунд, то duration
будет примерно 140 миллисекунд с параметром blockingDuration
равным (55 - 50) + (65 + 20). - 50) = 40 миллисекунд. В течение 40 миллисекунд в течение этого 140-миллисекундного кадра анимации считалось, что кадр заблокирован для обработки ввода.
Стоит ли смотреть на duration
или blockingDuration
Для обычного дисплея с частотой 60 Гц браузер будет пытаться запланировать кадр как минимум каждые 16,66 миллисекунды (чтобы обеспечить плавное обновление) или после задачи с высоким приоритетом, такой как обработка ввода (чтобы обеспечить быстрое обновление). Однако если нет ввода (как и других задач с высоким приоритетом), но есть очередь других задач, браузер, как правило, продолжит текущий кадр значительно позже 16,66 миллисекунды, независимо от того, насколько хорошо разбиты задачи внутри него. То есть браузер всегда будет пытаться расставить приоритеты ввода, но может предпочесть выполнение очереди задач вместо обновлений рендеринга. Это связано с тем, что рендеринг является дорогостоящим процессом, поэтому обработка комбинированной задачи рендеринга для нескольких задач обычно приводит к общему сокращению работы.
Таким образом, длинные кадры анимации с низким или нулевым значением blockingDuration
по-прежнему должны реагировать на ввод. Таким образом, сокращение или устранение blockingDuration
за счет разделения длительных задач является ключом к улучшению скорости реагирования, измеряемой INP.
Однако большое количество длинных кадров анимации, независимо от blockingDuration
указывает на то, что обновления пользовательского интерфейса задерживаются и поэтому все равно могут влиять на плавность и приводить к ощущению замедления пользовательского интерфейса при прокрутке или анимации, даже если это не является проблемой для отзывчивости, поскольку измеряется INP. Чтобы понять проблемы в этой области, обратите внимание на duration
, но ее может быть сложнее оптимизировать, поскольку вы не можете решить эту проблему, разбивая работу, а вместо этого должны ее сократить.
Тайминги кадров
Ранее упомянутые временные метки позволяют разделить длинный кадр анимации на тайминги:
Тайминг | Расчет |
---|---|
Время начала | startTime |
Конец Времени | startTime + duration |
Продолжительность работы | renderStart ? renderStart - startTime : duration |
Продолжительность рендеринга | renderStart ? (startTime + duration) - renderStart: 0 |
Рендеринг: продолжительность предварительного макета | styleAndLayoutStart ? styleAndLayoutStart - renderStart : 0 |
Рендеринг: продолжительность стиля и макета | styleAndLayoutStart ? (startTime + duration) - styleAndLayoutStart : 0 |
Улучшенная атрибуция сценария
Тип записи long-animation-frame
включает более точные данные об атрибуции каждого сценария, который способствовал созданию длинного кадра анимации (для сценариев продолжительностью более 5 миллисекунд).
Подобно API длинных задач, он будет предоставлен в виде массива записей атрибуции, каждая из которых содержит подробную информацию:
-
name
иEntryType
вернутscript
. - Осмысленный
invoker
, указывающий, как был вызван сценарий (например,'IMG#id.onload'
,'Window.requestAnimationFrame'
или'Response.json.then'
). -
invokerType
точки входа скрипта:-
user-callback
: известный обратный вызов, зарегистрированный из API веб-платформы (например,setTimeout
,requestAnimationFrame
). -
event-listener
: прослушиватель событий платформы (например,click
,load
,keyup
). -
resolve-promise
: Обработчик обещания платформы (например,fetch()
. Обратите внимание, что в случае с обещаниями все обработчики одних и тех же обещаний смешиваются в один «скрипт»).
-
reject-promise
: согласноresolve-promise
, но для отклонения. -
classic-script
: оценка сценария (например,<script>
илиimport()
) -
module-script
: То же, что иclassic-script
, но для скриптов модуля.
-
- Отдельные данные о времени для этого сценария:
-
startTime
: время вызова функции входа. -
duration
: Продолжительность междуstartTime
и моментом завершения обработки последующей очереди микрозадач. -
executionStart
: время после компиляции. -
forcedStyleAndLayoutDuration
: общее время, затраченное на обработку принудительного макета и стиля внутри этой функции (см. перебор ). -
pauseDuration
: общее время, потраченное на «приостановку» синхронных операций (предупреждение, синхронный XHR).
-
- Детали источника скрипта:
-
sourceURL
: имя ресурса скрипта, если оно доступно (или пустое, если не найдено). -
sourceFunctionName
: имя функции скрипта, если оно доступно (или пустое, если не найдено). -
sourceCharPosition
: позиция символа сценария, если она доступна (или -1, если не найдена).
-
-
windowAttribution
: контейнер (документ верхнего уровня или<iframe>
), в котором произошел длинный кадр анимации. -
window
: ссылка на окно того же происхождения.
Исходные записи, если они предусмотрены, позволяют разработчикам точно знать, как был вызван каждый сценарий в длинном кадре анимации, вплоть до позиции символа в вызывающем сценарии. Это дает точное местоположение в ресурсе JavaScript, который привел к созданию длинного кадра анимации.
Пример записи производительности long-animation-frame
Полный пример записи производительности long-animation-frame
, содержащий один сценарий:
{
"blockingDuration": 0,
"duration": 60,
"entryType": "long-animation-frame",
"firstUIEventTimestamp": 11801.099999999627,
"name": "long-animation-frame",
"renderStart": 11858.800000000745,
"scripts": [
{
"duration": 45,
"entryType": "script",
"executionStart": 11803.199999999255,
"forcedStyleAndLayoutDuration": 0,
"invoker": "DOMWindow.onclick",
"invokerType": "event-listener",
"name": "script",
"pauseDuration": 0,
"sourceURL": "https://web.dev/js/index-ffde4443.js",
"sourceFunctionName": "myClickHandler",
"sourceCharPosition": 17796,
"startTime": 11803.199999999255,
"window": [Window object],
"windowAttribution": "self"
}
],
"startTime": 11802.400000000373,
"styleAndLayoutStart": 11858.800000000745
}
Как можно видеть, это дает веб-сайтам беспрецедентный объем данных, позволяющий понять причину задержек при обновлении рендеринга.
Используйте API длинных кадров анимации в полевых условиях
Такие инструменты, как Chrome DevTools и Lighthouse, хотя и полезны для обнаружения и воспроизведения проблем, являются лабораторными инструментами, которые могут упускать из виду важные аспекты пользовательского опыта, которые могут предоставить только полевые данные.
API Long Animation Frames предназначен для использования в полевых условиях для сбора важных контекстных данных для взаимодействия с пользователем, которые не удалось получить с помощью API Long Tasks. Это может помочь вам выявить и воспроизвести проблемы с интерактивностью, которые иначе вы могли бы не обнаружить.
Функция определения поддержки API длинных кадров анимации
Вы можете использовать следующий код, чтобы проверить, поддерживается ли API:
if (PerformanceObserver.supportedEntryTypes.includes('long-animation-frame')) {
// Monitor LoAFs
}
Ссылка на самое продолжительное взаимодействие с INP
Наиболее очевидным вариантом использования API длинных кадров анимации является помощь в диагностике и устранении проблем взаимодействия с следующей отрисовкой (INP) , и это было одной из ключевых причин, по которой команда Chrome разработала этот API. Хороший INP — это тот, где на все взаимодействия отвечает 200 миллисекунд или меньше от взаимодействия до рисования кадра, а поскольку API Long Animation Frames измеряет все кадры, которые занимают 50 мс или более, большинство проблемных INP должны включать данные LoAF, чтобы помочь вам диагностировать. эти взаимодействия.
«INP LoAF» — это LoAF, который включает взаимодействие INP, как показано на следующей схеме:
В некоторых случаях событие INP может охватывать два LoAF — обычно, если взаимодействие происходит после того, как кадр начал часть рендеринга предыдущего кадра, и поэтому обработчик событий обрабатывается в следующем кадре:
В некоторых редких случаях возможно даже, что он может охватывать более двух LoAF.
Запись данных LoAF, связанных с взаимодействием INP, позволяет получить гораздо больше информации о взаимодействии INP, чтобы помочь в его диагностике. Это особенно полезно для понимания задержки ввода : вы можете увидеть, какие другие сценарии выполнялись в этом кадре.
Также может быть полезно понять необъяснимую продолжительность обработки и задержку представления , если ваши обработчики событий не воспроизводят значения, наблюдаемые для них, поскольку для ваших пользователей могут выполняться другие сценарии, которые могут не быть включены в ваше собственное тестирование.
Прямого API для связывания записи INP с связанной с ней записью или записями LoAF не существует, хотя это можно сделать в коде, сравнивая время начала и окончания каждой из них (см. пример сценарияWhyNp ). Библиотека web-vitals
включает все пересекающиеся LoAF в свойстве longAnimationFramesEntries
интерфейса атрибуции INP из версии 4.
После того как вы связали запись или записи LoAF, вы можете включить информацию с указанием авторства INP. Объект scripts
содержит наиболее ценную информацию, поскольку он может показать, что еще выполнялось в этих кадрах, поэтому передача этих данных в вашу службу аналитики позволит вам лучше понять, почему взаимодействие было медленным.
Отчеты о LoAF для взаимодействия с INP — это хороший способ выявить наиболее актуальные проблемы интерактивности на вашей странице. Каждый пользователь может по-разному взаимодействовать с вашей страницей, и при наличии достаточного объема данных атрибуции INP в данные атрибуции INP будет включен ряд потенциальных проблем. Это позволяет сортировать сценарии по объему, чтобы увидеть, какие сценарии коррелируют с медленным INP.
Отправляйте более длинные данные анимации обратно в конечную точку аналитики.
Недостатком рассмотрения только LoAF INP является то, что вы можете пропустить другие потенциальные области для улучшений, которые могут вызвать будущие проблемы с INP. Это может привести к ощущению погони за своим хвостом, когда вы исправляете проблему с INP, ожидая увидеть огромное улучшение, но обнаруживаете, что следующее самое медленное взаимодействие лишь немного лучше, чем это, поэтому ваш INP не сильно улучшится.
Поэтому вместо того, чтобы рассматривать только LoAF INP, вы можете рассмотреть все LoAF на протяжении всего срока службы страницы:
Однако каждая запись LoAF содержит значительный объем данных, поэтому вы, вероятно, захотите ограничить свой анализ только некоторыми LoAF. Кроме того, поскольку записи длинных кадров анимации могут быть довольно большими, разработчикам следует решить, какие данные из записи следует отправлять в аналитику. Например, общее время записи и, возможно, имена сценариев или какой-либо другой минимальный набор других контекстных данных, которые могут быть сочтены необходимыми.
Некоторые предлагаемые шаблоны для уменьшения объема данных длинных кадров анимации включают в себя:
- Наблюдайте за длинными кадрами анимации с взаимодействиями.
- Наблюдайте за длинными кадрами анимации с высокой длительностью блокировки.
- Наблюдайте за длинными кадрами анимации во время критических обновлений пользовательского интерфейса, чтобы улучшить плавность
- Наблюдайте за худшими длинными кадрами анимации
- Выявление общих закономерностей в длинных кадрах анимации.
Какой из этих шаблонов подойдет вам лучше всего, зависит от того, насколько далеко вы продвинулись на пути к оптимизации и насколько распространены длинные кадры анимации. Для сайта, который никогда раньше не оптимизировался по отзывчивости, может быть много LoAF. Возможно, вы захотите ограничиться только LoAF с взаимодействиями, или установить высокий порог, или смотреть только на худшие из них.
По мере того, как вы решаете свои распространенные проблемы с реагированием, вы можете расширить это, не ограничиваясь только взаимодействиями или высокой продолжительностью блокировки, или снизив пороговые значения.
Наблюдайте за длинными кадрами анимации с взаимодействиями.
Чтобы получить информацию, выходящую за рамки длинного кадра анимации INP, вы можете просмотреть все LoAF с взаимодействиями (которые можно обнаружить по наличию значения firstUIEventTimestamp
) с высоким blockingDuration
.
Это также может быть более простой метод мониторинга LoAF INP, вместо того, чтобы пытаться сопоставить их, что может быть более сложным. В большинстве случаев это будет включать LoAF INP для данного посещения, а в редких случаях, когда это не так, все равно возникают длительные взаимодействия, которые важно исправить, поскольку они могут быть взаимодействием INP для других пользователей.
Следующий код регистрирует все записи LoAF с параметром blockingDuration
более 100 миллисекунд, в которых произошло взаимодействие во время кадра. Здесь выбрано значение 100, поскольку оно меньше порога «хорошего» INP в 200 миллисекунд. Вы можете выбрать большее или меньшее значение в зависимости от ваших потребностей.
const REPORTING_THRESHOLD_MS = 100;
const observer = new PerformanceObserver(list => {
for (const entry of list.getEntries()) {
if (entry.blockingDuration > REPORTING_THRESHOLD_MS &&
entry.firstUIEventTimestamp > 0
) {
// Example here logs to console, but could also report back to analytics
console.log(entry);
}
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
Наблюдайте за длинными кадрами анимации с высокой длительностью блокировки.
В целях улучшения просмотра всех длинных кадров анимации с взаимодействиями вы можете просмотреть все длинные кадры анимации с большой продолжительностью блокировки. Это указывает на потенциальные проблемы INP, если пользователь взаимодействует во время этих длинных кадров анимации.
Следующий код регистрирует все записи LoAF с длительностью блокировки более 100 миллисекунд, когда взаимодействие произошло во время кадра. Здесь выбрано значение 100, поскольку оно меньше порога «хорошего» INP в 200 миллисекунд, что помогает выявить потенциальные проблемные кадры, сохраняя при этом количество длинных кадров анимации, о которых сообщается, до минимума. Вы можете выбрать большее или меньшее значение в зависимости от ваших потребностей.
const REPORTING_THRESHOLD_MS = 100;
const observer = new PerformanceObserver(list => {
for (const entry of list.getEntries()) {
if (entry.blockingDuration > REPORTING_THRESHOLD_MS) {
// Example here logs to console, but could also report back to analytics
console.log(entry);
}
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
Наблюдайте за длинными кадрами анимации во время критических обновлений пользовательского интерфейса, чтобы улучшить плавность
Как упоминалось ранее, рассмотрение длинных кадров анимации с высокой длительностью блокировки может помочь решить проблему реагирования на ввод. Но для плавности следует смотреть на все длинные кадры анимации с большой duration
.
Поскольку это может стать довольно шумным, вы можете ограничить измерения ключевыми точками по такой схеме:
const REPORTING_THRESHOLD_MS = 100;
const observer = new PerformanceObserver(list => {
if (measureImportantUIupdate) {
for (const entry of list.getEntries()) {
if (entry.duration > REPORTING_THRESHOLD_MS) {
// Example here logs to console, but could also report back to analytics
console.log(entry);
}
}
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
async function doUIUpdatesWithMeasurements() {
measureImportantUIupdate = true;
await doUIUpdates();
measureImportantUIupdate = false;
}
Наблюдайте за худшими длинными кадрами анимации
Вместо того, чтобы устанавливать пороговое значение, сайты могут захотеть собирать данные о самом длинном кадре анимации (или кадрах), чтобы уменьшить объем данных, которые необходимо передать. Таким образом, независимо от того, сколько длинных кадров анимации отображается на странице, возвращаются только данные о худших — пяти, десяти или любом количестве длинных кадров анимации, которые абсолютно необходимы.
MAX_LOAFS_TO_CONSIDER = 10;
let longestBlockingLoAFs = [];
const observer = new PerformanceObserver(list => {
longestBlockingLoAFs = longestBlockingLoAFs.concat(list.getEntries()).sort(
(a, b) => b.blockingDuration - a.blockingDuration
).slice(0, MAX_LOAFS_TO_CONSIDER);
});
observer.observe({ type: 'long-animation-frame', buffered: true });
Эти стратегии также можно комбинировать — взгляните только на 10 худших LoAF с взаимодействиями продолжительностью более 100 миллисекунд.
В подходящее время ( в идеале при событии visibilitychange
) возвращайтесь к аналитике. Для локального тестирования вы можете периодически использовать console.table
:
console.table(longestBlockingLoAFs);
Выявление общих закономерностей в длинных кадрах анимации.
Альтернативной стратегией было бы рассмотреть распространенные сценарии, которые чаще всего встречаются в длинных записях кадров анимации. Данные могут передаваться на уровне сценария и позиции персонажа для выявления рецидивистов.
Это может работать особенно хорошо для настраиваемых платформ, где темы или плагины, вызывающие проблемы с производительностью, могут быть идентифицированы на нескольких сайтах.
Время выполнения общих сценариев (или сторонних источников) в длинных кадрах анимации можно суммировать и возвращать для определения общих участников длинных кадров анимации на сайте или в группе сайтов. Например, чтобы посмотреть URL-адреса:
const observer = new PerformanceObserver(list => {
const allScripts = list.getEntries().flatMap(entry => entry.scripts);
const scriptSource = [...new Set(allScripts.map(script => script.sourceURL))];
const scriptsBySource= scriptSource.map(sourceURL => ([sourceURL,
allScripts.filter(script => script.sourceURL === sourceURL)
]));
const processedScripts = scriptsBySource.map(([sourceURL, scripts]) => ({
sourceURL,
count: scripts.length,
totalDuration: scripts.reduce((subtotal, script) => subtotal + script.duration, 0)
}));
processedScripts.sort((a, b) => b.totalDuration - a.totalDuration);
// Example here logs to console, but could also report back to analytics
console.table(processedScripts);
});
observer.observe({type: 'long-animation-frame', buffered: true});
И пример этого вывода:
(index) | sourceURL | count | totalDuration |
---|---|---|---|
0 | 'https://example.consent.com/consent.js' | 1 | 840 |
1 | 'https://example.com/js/analytics.js' | 7 | 628 |
2 | 'https://example.chatapp.com/web-chat.js' | 1 | 5 |
Используйте API длинных кадров анимации в инструментах
API также предоставляет дополнительные инструменты разработчика для локальной отладки. Хотя некоторые инструменты, такие как Lighthouse и Chrome DevTools, смогли собрать большую часть этих данных, используя детали трассировки более низкого уровня, наличие этого API более высокого уровня может позволить другим инструментам получить доступ к этим данным.
Поверхностные данные кадров длинной анимации в DevTools
Вы можете отображать длинные кадры анимации в DevTools с помощью API performance.measure()
, которые затем отображаются на дорожке пользовательского времени DevTools в трассировках производительности, чтобы показать, на чем сосредоточить свои усилия для повышения производительности. Используя DevTools Extensibility API, их можно даже отобразить в отдельной дорожке:
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
performance.measure('LoAF', {
start: entry.startTime,
end: entry.startTime + entry.duration,
detail: {
devtools: {
dataType: "track-entry",
track: "Long animation frames",
trackGroup: "Performance Timeline",
color: "tertiary-dark",
tooltipText: 'LoAF'
}
}
});
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
В долгосрочной перспективе длинные кадры анимации, скорее всего, будут включены в сам DevTools, но предыдущий фрагмент кода позволяет пока разместить их там.
Первая запись на предыдущем рисунке также показывает, что браузер обрабатывает несколько задач одновременно в одном длинном кадре анимации, а не выполняет рендеринг между ними. Как упоминалось ранее, это может произойти, когда нет высокоприоритетных входных задач, но есть очередь задач. Первая длинная задача требует завершения некоторых обновлений рендеринга (в противном случае текущий длинный кадр анимации будет сброшен после нее, а новый начнется со следующей задачей), но вместо того, чтобы немедленно выполнить рендеринг, браузер обработал ряд дополнительные задачи и только затем выполнил задачу длительного рендеринга и завершил длинный кадр анимации. Это демонстрирует полезность просмотра длинных кадров анимации в DevTools, а не просто длинных задач, чтобы выявить задержку рендеринга.
Используйте данные длинных кадров анимации в других инструментах разработчика.
Расширение Web Vitals продемонстрировало ценность регистрации сводной отладочной информации для диагностики проблем с производительностью.
Теперь он также отображает данные длинных кадров анимации для каждого обратного вызова INP и каждого взаимодействия:
Используйте данные длинных кадров анимации в инструментах автоматического тестирования.
Аналогичным образом инструменты автоматического тестирования в конвейерах CI/CD могут выявить подробную информацию о потенциальных проблемах с производительностью, измеряя длинные кадры анимации во время выполнения различных наборов тестов.
Часто задаваемые вопросы
Некоторые из часто задаваемых вопросов по этому API включают в себя:
Почему бы просто не расширить или не усовершенствовать API длинных задач?
Это альтернативный взгляд на отчетность по схожим, но, в конечном счете, другим показателям потенциальных проблем с реагированием. Важно обеспечить, чтобы сайты, использующие существующий API длинных задач, продолжали функционировать, чтобы не нарушать существующие варианты использования.
Хотя API длинных задач может извлечь выгоду из некоторых функций LoAF (например, улучшенной модели атрибуции), мы считаем, что сосредоточение внимания на кадрах, а не на задачах, дает множество преимуществ, которые делают этот API фундаментально отличным от существующего API длинных задач.
Почему у меня нет записей сценария?
Это может указывать на то, что длинный кадр анимации возник не из-за JavaScipt, а из-за большой работы по рендерингу.
Это также может произойти, когда длинный кадр анимации вызван JavaScript, но атрибуция сценария не может быть предоставлена по различным причинам конфиденциальности, как отмечалось ранее (в первую очередь из-за того, что JavaScript не принадлежит странице).
Почему у меня есть записи сценария, но нет или ограничена исходная информация?
Это может произойти по ряду причин, в том числе из-за отсутствия хорошего источника, на который можно было бы указать .
Информация о скрипте также будет ограничена только sourceURL
(исключая любые перенаправления) для скриптов no-cors cross-origin
, с пустой строкой для sourceFunctionName
и -1
для sourceCharPosition
. Эту проблему можно решить, извлекая эти сценарии с помощью CORS, добавив crossOrigin = "anonymous"
к вызову <script>
.
Например, скрипт Диспетчера тегов Google по умолчанию, добавляемый на страницу:
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');</script>
<!-- End Google Tag Manager -->
Можно улучшить, добавив j.crossOrigin = "anonymous"
, чтобы обеспечить предоставление полной информации об авторстве для GTM.
Заменит ли это API длинных задач?
Хотя мы считаем, что API Long Animation Frames является лучшим и более полным API для измерения длинных задач, в настоящее время мы не планируем прекращать поддержку API Long Tasks.
Требуется обратная связь
Отзывы можно оставить в списке проблем GitHub , а ошибки в реализации API Chrome можно зарегистрировать в системе отслеживания проблем Chrome .
Заключение
API Long Animation Frames — это потрясающий новый API со многими потенциальными преимуществами по сравнению с предыдущим API Long Tasks.
Это оказывается ключевым инструментом для решения проблем реагирования, измеряемых INP. INP — это сложный показатель для оптимизации, и этот API — один из способов, с помощью которого команда Chrome стремится упростить выявление и решение проблем для разработчиков.
Однако сфера применения API Long Animation Frames выходит за рамки просто INP и может помочь выявить другие причины медленных обновлений, которые могут повлиять на общую плавность взаимодействия с пользователем веб-сайта.
Благодарности
Миниатюрное изображение Генри Би на Unsplash .
,API длинных кадров анимации (LoAF произносится как Lo-Af) — это обновление API длинных задач, позволяющее лучше понять медленные обновления пользовательского интерфейса (UI). Это может быть полезно для выявления кадров медленной анимации, которые могут повлиять на метрику Interaction to Next Paint (INP) Core Web Vital, которая измеряет скорость реагирования, или для выявления других ошибок пользовательского интерфейса, влияющих на плавность .
Статус API
После первоначального пробного перехода от Chrome 116 к Chrome 122 API LoAF был переведен из Chrome 123 .
Предыстория: API длинных задач
API Long Animation Frames — это альтернатива API Long Tasks, который уже некоторое время доступен в Chrome (начиная с Chrome 58). Как следует из названия, API длинных задач позволяет отслеживать длинные задачи, то есть задачи, которые занимают основной поток в течение 50 миллисекунд или дольше. Длинные задачи можно отслеживать с помощью интерфейса PerformanceLongTaskTiming
с помощью PeformanceObserver
:
const observer = new PerformanceObserver((list) => {
console.log(list.getEntries());
});
observer.observe({ type: 'longtask', buffered: true });
Длинные задачи могут вызвать проблемы с реагированием. Если пользователь пытается взаимодействовать со страницей (например, нажать кнопку или открыть меню), но основной поток уже выполняет длинную задачу, то взаимодействие пользователя задерживается в ожидании завершения этой задачи.
Чтобы улучшить скорость реагирования, часто советуют разбивать длинные задачи . Если вместо этого каждая длинная задача разбивается на серию нескольких более мелких задач, это может позволить выполнять более важные задачи между ними, чтобы избежать значительных задержек в реагировании на взаимодействия.
Поэтому при попытке улучшить скорость реагирования в первую очередь часто приходится запускать трассировку производительности и анализировать длинные задачи. Это можно сделать с помощью лабораторного инструмента аудита, такого как Lighthouse (который имеет функцию « Избегать длинных задач основного потока »), или путем просмотра длинных задач в Chrome DevTools .
Лабораторное тестирование часто является плохой отправной точкой для выявления проблем с реагированием , поскольку эти инструменты могут не учитывать взаимодействия, а если и включают, то представляют собой небольшую подгруппу вероятных взаимодействий. В идеале вы должны измерить причины медленного взаимодействия в полевых условиях.
Недостатки API длинных задач
Измерение длительных задач в полевых условиях с помощью Performance Observer приносит лишь некоторую пользу. На самом деле он не дает столько информации, кроме того факта, что была выполнена длинная задача и сколько времени она заняла.
Инструменты реального мониторинга пользователей (RUM) часто используют это для отслеживания количества или продолжительности длительных задач или определения того, на каких страницах они выполняются, но без основных подробностей о том, что вызвало длительную задачу, это имеет лишь ограниченное применение. API длинных задач имеет только базовую модель атрибуции , которая в лучшем случае сообщает вам только контейнер, в котором возникла длинная задача (документ верхнего уровня или <iframe>
), но не сценарий или функцию, вызвавшую ее, как показано на рисунке. типичная запись:
{
"name": "unknown",
"entryType": "longtask",
"startTime": 31.799999997019768,
"duration": 136,
"attribution": [
{
"name": "unknown",
"entryType": "taskattribution",
"startTime": 0,
"duration": 0,
"containerType": "window",
"containerSrc": "",
"containerId": "",
"containerName": ""
}
]
}
API длинных задач также является неполным представлением, поскольку также может исключать некоторые важные задачи. Некоторые обновления, такие как рендеринг, происходят в отдельных задачах, которые в идеале должны быть включены вместе с предыдущим выполнением, чтобы это обновление точно измеряло «общую работу» для этого взаимодействия. Более подробную информацию об ограничениях, связанных с использованием задач, см . в разделе пояснения «Когда не хватает длинных задач» .
Последняя проблема заключается в том, что измерение длительных задач сообщает только об отдельных задачах, длительность которых превышает лимит в 50 миллисекунд. Кадр анимации может состоять из нескольких задач, меньших по времени, чем этот предел в 50 миллисекунд, но в совокупности все равно блокировать возможность рендеринга браузера.
API длинных кадров анимации
API Long Animation Frames (LoAF) — это новый API, который призван устранить некоторые недостатки API Long Tasks, чтобы дать разработчикам возможность получить более полезную информацию, которая поможет решить проблемы с быстродействием и улучшить INP, а также получить представление о проблемах плавности. .
Хорошая отзывчивость означает, что страница быстро реагирует на взаимодействия с ней. Это предполагает возможность своевременно создавать любые обновления, необходимые пользователю, и избегать блокировки этих обновлений. Для INP рекомендуется отвечать за 200 миллисекунд или меньше , но для других обновлений (например, анимации) даже 200 миллисекунд могут оказаться слишком долгими.
API длинных кадров анимации — это альтернативный подход к измерению работы блокировки. Вместо измерения отдельных задач API Long Animation Frames, как следует из названия, измеряет длинные кадры анимации . Длинный кадр анимации — это когда обновление рендеринга задерживается более чем на 50 миллисекунд (так же, как пороговое значение для API длинных задач).
Длинные кадры анимации отсчитываются от начала задач, требующих рендеринга. Если первая задача в потенциально длинном кадре анимации не требует рендеринга, длинный кадр анимации завершается после завершения задачи, не связанной с рендерингом, и новый потенциальный длинный кадр анимации начинается со следующей задачи. Такие длинные кадры анимации без рендеринга по-прежнему включаются в API длинных кадров анимации, если их длительность превышает 50 миллисекунд (со временем renderStart
равным 0), чтобы можно было измерить потенциально блокирующую работу.
Длинные кадры анимации можно наблюдать так же, как и длинные задачи, с помощью PerformanceObserver
, но вместо этого обращая внимание на тип long-animation-frame
:
const observer = new PerformanceObserver((list) => {
console.log(list.getEntries());
});
observer.observe({ type: 'long-animation-frame', buffered: true });
Предыдущие длинные кадры анимации также можно запросить на временной шкале производительности следующим образом:
const loafs = performance.getEntriesByType('long-animation-frame');
Однако для записей производительности существует maxBufferSize
, после которого новые записи удаляются, поэтому рекомендуемым подходом является подход PerformanceObserver. Размер буфера long-animation-frame
равен 200, как и для long-tasks
.
Преимущества просмотра фреймов вместо задач
Ключевое преимущество рассмотрения этого с точки зрения кадра, а не с точки зрения задач, заключается в том, что длинная анимация может состоять из любого количества задач, которые в совокупности привели к созданию длинного кадра анимации. Это решает последний момент, упомянутый ранее, когда сумма многих более мелких задач, блокирующих рендеринг, перед кадром анимации может не отображаться API длинных задач.
Еще одним преимуществом этого альтернативного подхода к длительным задачам является возможность разбивки по времени всего кадра. Вместо того, чтобы просто включать startTime
и duration
, как API длинных задач, LoAF включает гораздо более подробную разбивку различных частей длительности кадра.
Временные метки и продолжительность кадров
-
startTime
: время начала длинного кадра анимации относительно времени начала навигации. -
duration
: продолжительность длинного кадра анимации (не включая время презентации). -
renderStart
: время начала цикла рендеринга, который включает в себя обратные вызовыrequestAnimationFrame
, расчет стиля и макета, обратные вызовы наблюдателя изменения размера и наблюдателя пересечения. -
styleAndLayoutStart
: начало периода времени, затраченного на расчеты стиля и макета. -
firstUIEventTimestamp
: время первого события пользовательского интерфейса (мыши/клавиатуры и т. д.), которое будет обработано в течение этого кадра. -
blockingDuration
: общая продолжительность в миллисекундах, в течение которой кадр анимации будет блокировать обработку ввода или другие задачи с высоким приоритетом.
Объяснение blockingDuration
Длинный кадр анимации может состоять из нескольких задач. blockingDuration
— это сумма длительности задачи, превышающей 50 миллисекунд (включая окончательную продолжительность рендеринга в самой длинной задаче).
Например, если длинный кадр анимации состоит из двух задач по 55 миллисекунд и 65 миллисекунд, за которыми следует рендеринг продолжительностью 20 миллисекунд, то duration
будет примерно 140 миллисекунд с параметром blockingDuration
равным (55 - 50) + (65 + 20). - 50) = 40 миллисекунд. В течение 40 миллисекунд в течение этого 140-миллисекундного кадра анимации считалось, что кадр заблокирован для обработки ввода.
Стоит ли смотреть на duration
или blockingDuration
Для обычного дисплея с частотой 60 Гц браузер будет пытаться запланировать кадр как минимум каждые 16,66 миллисекунды (чтобы обеспечить плавное обновление) или после задачи с высоким приоритетом, такой как обработка ввода (чтобы обеспечить быстрое обновление). Однако если нет ввода (как и других задач с высоким приоритетом), но есть очередь других задач, браузер, как правило, продолжит текущий кадр значительно позже 16,66 миллисекунды, независимо от того, насколько хорошо разбиты задачи внутри него. То есть браузер всегда будет пытаться расставить приоритеты ввода, но может предпочесть выполнение очереди задач вместо обновлений рендеринга. Это связано с тем, что рендеринг является дорогостоящим процессом, поэтому обработка комбинированной задачи рендеринга для нескольких задач обычно приводит к общему сокращению работы.
Таким образом, длинные кадры анимации с низким или нулевым значением blockingDuration
по-прежнему должны реагировать на ввод. Таким образом, сокращение или устранение blockingDuration
за счет разделения длительных задач является ключом к улучшению скорости реагирования, измеряемой INP.
Однако большое количество длинных кадров анимации, независимо от blockingDuration
указывает на то, что обновления пользовательского интерфейса задерживаются и поэтому все равно могут влиять на плавность и приводить к ощущению замедления пользовательского интерфейса при прокрутке или анимации, даже если это не является проблемой для отзывчивости, поскольку измеряется INP. Чтобы понять проблемы в этой области, обратите внимание на duration
, но ее может быть сложнее оптимизировать, поскольку вы не можете решить эту проблему, разбивая работу, а вместо этого должны ее сократить.
Тайминги кадров
Ранее упомянутые временные метки позволяют разделить длинный кадр анимации на тайминги:
Тайминг | Расчет |
---|---|
Время начала | startTime |
Конец Времени | startTime + duration |
Продолжительность работы | renderStart ? renderStart - startTime : duration |
Продолжительность рендеринга | renderStart ? (startTime + duration) - renderStart: 0 |
Рендеринг: продолжительность предварительного макета | styleAndLayoutStart ? styleAndLayoutStart - renderStart : 0 |
Рендеринг: продолжительность стиля и макета | styleAndLayoutStart ? (startTime + duration) - styleAndLayoutStart : 0 |
Улучшенная атрибуция сценария
Тип записи long-animation-frame
включает более точные данные об атрибуции каждого сценария, который способствовал созданию длинного кадра анимации (для сценариев продолжительностью более 5 миллисекунд).
Подобно API длинных задач, он будет предоставлен в виде массива записей атрибуции, каждая из которых содержит подробную информацию:
-
name
иEntryType
вернутscript
. - Осмысленный
invoker
, указывающий, как был вызван сценарий (например,'IMG#id.onload'
,'Window.requestAnimationFrame'
или'Response.json.then'
). -
invokerType
точки входа скрипта:-
user-callback
: известный обратный вызов, зарегистрированный из API веб-платформы (например,setTimeout
,requestAnimationFrame
). -
event-listener
: прослушиватель событий платформы (например,click
,load
,keyup
). -
resolve-promise
: Обработчик обещания платформы (например,fetch()
. Обратите внимание, что в случае с обещаниями все обработчики одних и тех же обещаний смешиваются в один «скрипт»).
-
reject-promise
: согласноresolve-promise
, но для отклонения. -
classic-script
: оценка сценария (например,<script>
илиimport()
) -
module-script
: То же, что иclassic-script
, но для скриптов модуля.
-
- Отдельные данные о времени для этого сценария:
-
startTime
: время вызова функции входа. -
duration
: Продолжительность междуstartTime
и моментом завершения обработки последующей очереди микрозадач. -
executionStart
: время после компиляции. -
forcedStyleAndLayoutDuration
: общее время, затраченное на обработку принудительного макета и стиля внутри этой функции (см. перебор ). -
pauseDuration
: общее время, потраченное на «приостановку» синхронных операций (предупреждение, синхронный XHR).
-
- Детали источника скрипта:
-
sourceURL
: имя ресурса скрипта, если оно доступно (или пустое, если не найдено). -
sourceFunctionName
: имя функции скрипта, если оно доступно (или пустое, если не найдено). -
sourceCharPosition
: позиция символа сценария, если она доступна (или -1, если не найдена).
-
-
windowAttribution
: контейнер (документ верхнего уровня или<iframe>
), в котором произошел длинный кадр анимации. -
window
: ссылка на окно того же происхождения.
Исходные записи, если они предусмотрены, позволяют разработчикам точно знать, как был вызван каждый сценарий в длинном кадре анимации, вплоть до позиции символа в вызывающем сценарии. Это дает точное местоположение в ресурсе JavaScript, который привел к созданию длинного кадра анимации.
Пример записи производительности long-animation-frame
Полный пример записи производительности long-animation-frame
, содержащий один сценарий:
{
"blockingDuration": 0,
"duration": 60,
"entryType": "long-animation-frame",
"firstUIEventTimestamp": 11801.099999999627,
"name": "long-animation-frame",
"renderStart": 11858.800000000745,
"scripts": [
{
"duration": 45,
"entryType": "script",
"executionStart": 11803.199999999255,
"forcedStyleAndLayoutDuration": 0,
"invoker": "DOMWindow.onclick",
"invokerType": "event-listener",
"name": "script",
"pauseDuration": 0,
"sourceURL": "https://web.dev/js/index-ffde4443.js",
"sourceFunctionName": "myClickHandler",
"sourceCharPosition": 17796,
"startTime": 11803.199999999255,
"window": [Window object],
"windowAttribution": "self"
}
],
"startTime": 11802.400000000373,
"styleAndLayoutStart": 11858.800000000745
}
Как можно видеть, это дает веб-сайтам беспрецедентный объем данных, позволяющий понять причину задержек при обновлении рендеринга.
Используйте API длинных кадров анимации в полевых условиях
Такие инструменты, как Chrome DevTools и Lighthouse, хотя и полезны для обнаружения и воспроизведения проблем, являются лабораторными инструментами, которые могут упускать из виду важные аспекты пользовательского опыта, которые могут предоставить только полевые данные.
API Long Animation Frames предназначен для использования в полевых условиях для сбора важных контекстных данных для взаимодействия с пользователем, которые не удалось получить с помощью API Long Tasks. Это может помочь вам выявить и воспроизвести проблемы с интерактивностью, которые иначе вы могли бы не обнаружить.
Функция определения поддержки API длинных кадров анимации
Вы можете использовать следующий код, чтобы проверить, поддерживается ли API:
if (PerformanceObserver.supportedEntryTypes.includes('long-animation-frame')) {
// Monitor LoAFs
}
Ссылка на самое продолжительное взаимодействие с INP
Наиболее очевидным вариантом использования API длинных кадров анимации является помощь в диагностике и устранении проблем взаимодействия с следующей отрисовкой (INP) , и это было одной из ключевых причин, по которой команда Chrome разработала этот API. Хороший INP — это тот, где на все взаимодействия отвечает 200 миллисекунд или меньше от взаимодействия до рисования кадра, а поскольку API Long Animation Frames измеряет все кадры, которые занимают 50 мс или более, большинство проблемных INP должны включать данные LoAF, чтобы помочь вам диагностировать. эти взаимодействия.
«INP LoAF» — это LoAF, который включает взаимодействие INP, как показано на следующей схеме:
В некоторых случаях событие INP может охватывать два LoAF — обычно, если взаимодействие происходит после того, как кадр начал часть рендеринга предыдущего кадра, и поэтому обработчик событий обрабатывается в следующем кадре:
В некоторых редких случаях возможно даже, что он может охватывать более двух LoAF.
Запись данных LoAF, связанных с взаимодействием INP, позволяет получить гораздо больше информации о взаимодействии INP, чтобы помочь в его диагностике. Это особенно полезно для понимания задержки ввода : вы можете увидеть, какие другие сценарии выполнялись в этом кадре.
Также может быть полезно понять необъяснимую продолжительность обработки и задержку представления , если ваши обработчики событий не воспроизводят значения, наблюдаемые для них, поскольку для ваших пользователей могут выполняться другие сценарии, которые могут не быть включены в ваше собственное тестирование.
Прямого API для связывания записи INP с связанной с ней записью или записями LoAF не существует, хотя это можно сделать в коде, сравнивая время начала и окончания каждой из них (см. пример сценарияWhyNp ). Библиотека web-vitals
включает все пересекающиеся LoAF в свойстве longAnimationFramesEntries
интерфейса атрибуции INP из версии 4.
После того как вы связали запись или записи LoAF, вы можете включить информацию с указанием авторства INP. Объект scripts
содержит наиболее ценную информацию, поскольку он может показать, что еще выполнялось в этих кадрах, поэтому передача этих данных в вашу службу аналитики позволит вам лучше понять, почему взаимодействие было медленным.
Отчеты о LoAF для взаимодействия с INP — это хороший способ выявить наиболее актуальные проблемы интерактивности на вашей странице. Каждый пользователь может по-разному взаимодействовать с вашей страницей, и при наличии достаточного объема данных атрибуции INP в данные атрибуции INP будет включен ряд потенциальных проблем. Это позволяет сортировать сценарии по объему, чтобы увидеть, какие сценарии коррелируют с медленным INP.
Отправляйте более длинные данные анимации обратно в конечную точку аналитики.
Недостатком рассмотрения только LoAF INP является то, что вы можете пропустить другие потенциальные области для улучшений, которые могут вызвать будущие проблемы с INP. Это может привести к ощущению погони за своим хвостом, когда вы исправляете проблему с INP, ожидая увидеть огромное улучшение, но обнаруживаете, что следующее самое медленное взаимодействие лишь немного лучше, чем это, поэтому ваш INP не сильно улучшится.
Поэтому вместо того, чтобы рассматривать только LoAF INP, вы можете рассмотреть все LoAF на протяжении всего срока службы страницы:
Однако каждая запись LoAF содержит значительный объем данных, поэтому вы, вероятно, захотите ограничить свой анализ только некоторыми LoAF. Кроме того, поскольку записи длинных кадров анимации могут быть довольно большими, разработчикам следует решить, какие данные из записи следует отправлять в аналитику. Например, общее время записи и, возможно, имена сценариев или какой-либо другой минимальный набор других контекстных данных, которые могут быть сочтены необходимыми.
Некоторые предлагаемые шаблоны для уменьшения объема данных длинных кадров анимации включают в себя:
- Наблюдайте за длинными кадрами анимации с взаимодействиями.
- Наблюдайте за длинными кадрами анимации с высокой длительностью блокировки.
- Наблюдайте за длинными кадрами анимации во время критических обновлений пользовательского интерфейса, чтобы улучшить плавность
- Наблюдайте за худшими длинными кадрами анимации
- Выявление общих закономерностей в длинных кадрах анимации.
Какой из этих шаблонов подойдет вам лучше всего, зависит от того, насколько далеко вы продвинулись на пути к оптимизации и насколько распространены длинные кадры анимации. Для сайта, который никогда раньше не оптимизировался по отзывчивости, может быть много LoAF. Возможно, вы захотите ограничиться только LoAF с взаимодействиями, или установить высокий порог, или смотреть только на худшие из них.
По мере того, как вы решаете свои распространенные проблемы с реагированием, вы можете расширить это, не ограничиваясь только взаимодействиями или высокой продолжительностью блокировки, или снизив пороговые значения.
Наблюдайте за длинными кадрами анимации с взаимодействиями.
Чтобы получить информацию, выходящую за рамки длинного кадра анимации INP, вы можете просмотреть все LoAF с взаимодействиями (которые можно обнаружить по наличию значения firstUIEventTimestamp
) с высоким blockingDuration
.
Это также может быть более простой метод мониторинга LoAF INP, вместо того, чтобы пытаться сопоставить их, что может быть более сложным. В большинстве случаев это будет включать LoAF INP для данного посещения, а в редких случаях, когда это не так, все равно возникают длительные взаимодействия, которые важно исправить, поскольку они могут быть взаимодействием INP для других пользователей.
Следующий код регистрирует все записи LoAF с параметром blockingDuration
более 100 миллисекунд, в которых произошло взаимодействие во время кадра. Здесь выбрано значение 100, поскольку оно меньше порога «хорошего» INP в 200 миллисекунд. Вы можете выбрать большее или меньшее значение в зависимости от ваших потребностей.
const REPORTING_THRESHOLD_MS = 100;
const observer = new PerformanceObserver(list => {
for (const entry of list.getEntries()) {
if (entry.blockingDuration > REPORTING_THRESHOLD_MS &&
entry.firstUIEventTimestamp > 0
) {
// Example here logs to console, but could also report back to analytics
console.log(entry);
}
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
Наблюдайте за длинными кадрами анимации с высокой длительностью блокировки.
В целях улучшения просмотра всех длинных кадров анимации с взаимодействиями вы можете просмотреть все длинные кадры анимации с большой продолжительностью блокировки. Это указывает на потенциальные проблемы INP, если пользователь взаимодействует во время этих длинных кадров анимации.
Следующий код регистрирует все записи LoAF с длительностью блокировки более 100 миллисекунд, когда взаимодействие произошло во время кадра. Здесь выбрано значение 100, поскольку оно меньше порога «хорошего» INP в 200 миллисекунд, что помогает выявить потенциальные проблемные кадры, сохраняя при этом количество длинных кадров анимации, о которых сообщается, до минимума. Вы можете выбрать большее или меньшее значение в зависимости от ваших потребностей.
const REPORTING_THRESHOLD_MS = 100;
const observer = new PerformanceObserver(list => {
for (const entry of list.getEntries()) {
if (entry.blockingDuration > REPORTING_THRESHOLD_MS) {
// Example here logs to console, but could also report back to analytics
console.log(entry);
}
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
Наблюдайте за длинными кадрами анимации во время критических обновлений пользовательского интерфейса, чтобы улучшить плавность
Как упоминалось ранее, рассмотрение длинных кадров анимации с высокой длительностью блокировки может помочь решить проблему реагирования на ввод. Но для плавности следует смотреть на все длинные кадры анимации с большой duration
.
Поскольку это может стать довольно шумным, вы можете ограничить измерения ключевыми точками по такой схеме:
const REPORTING_THRESHOLD_MS = 100;
const observer = new PerformanceObserver(list => {
if (measureImportantUIupdate) {
for (const entry of list.getEntries()) {
if (entry.duration > REPORTING_THRESHOLD_MS) {
// Example here logs to console, but could also report back to analytics
console.log(entry);
}
}
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
async function doUIUpdatesWithMeasurements() {
measureImportantUIupdate = true;
await doUIUpdates();
measureImportantUIupdate = false;
}
Наблюдайте за худшими длинными кадрами анимации
Вместо того, чтобы устанавливать пороговое значение, сайты могут захотеть собирать данные о самом длинном кадре анимации (или кадрах), чтобы уменьшить объем данных, которые необходимо передать. Таким образом, независимо от того, сколько длинных кадров анимации отображается на странице, возвращаются только данные о худших — пяти, десяти или любом количестве длинных кадров анимации, которые абсолютно необходимы.
MAX_LOAFS_TO_CONSIDER = 10;
let longestBlockingLoAFs = [];
const observer = new PerformanceObserver(list => {
longestBlockingLoAFs = longestBlockingLoAFs.concat(list.getEntries()).sort(
(a, b) => b.blockingDuration - a.blockingDuration
).slice(0, MAX_LOAFS_TO_CONSIDER);
});
observer.observe({ type: 'long-animation-frame', buffered: true });
Эти стратегии также можно комбинировать — взгляните только на 10 худших LoAF с взаимодействиями продолжительностью более 100 миллисекунд.
В подходящее время ( в идеале при событии visibilitychange
) возвращайтесь к аналитике. Для локального тестирования вы можете периодически использовать console.table
:
console.table(longestBlockingLoAFs);
Выявление общих закономерностей в длинных кадрах анимации.
Альтернативной стратегией было бы рассмотреть распространенные сценарии, которые чаще всего встречаются в длинных записях кадров анимации. Данные могут передаваться на уровне сценария и позиции персонажа для выявления рецидивистов.
Это может работать особенно хорошо для настраиваемых платформ, где темы или плагины, вызывающие проблемы с производительностью, могут быть идентифицированы на нескольких сайтах.
Время выполнения общих сценариев (или сторонних источников) в длинных кадрах анимации можно суммировать и возвращать для определения общих участников длинных кадров анимации на сайте или в группе сайтов. Например, чтобы посмотреть URL-адреса:
const observer = new PerformanceObserver(list => {
const allScripts = list.getEntries().flatMap(entry => entry.scripts);
const scriptSource = [...new Set(allScripts.map(script => script.sourceURL))];
const scriptsBySource= scriptSource.map(sourceURL => ([sourceURL,
allScripts.filter(script => script.sourceURL === sourceURL)
]));
const processedScripts = scriptsBySource.map(([sourceURL, scripts]) => ({
sourceURL,
count: scripts.length,
totalDuration: scripts.reduce((subtotal, script) => subtotal + script.duration, 0)
}));
processedScripts.sort((a, b) => b.totalDuration - a.totalDuration);
// Example here logs to console, but could also report back to analytics
console.table(processedScripts);
});
observer.observe({type: 'long-animation-frame', buffered: true});
И пример этого вывода:
(index) | sourceURL | count | totalDuration |
---|---|---|---|
0 | 'https://example.consent.com/consent.js' | 1 | 840 |
1 | 'https://example.com/js/analytics.js' | 7 | 628 |
2 | 'https://example.chatapp.com/web-chat.js' | 1 | 5 |
Используйте API длинных кадров анимации в инструментах
API также предоставляет дополнительные инструменты разработчика для локальной отладки. Хотя некоторые инструменты, такие как Lighthouse и Chrome DevTools, смогли собрать большую часть этих данных, используя детали трассировки более низкого уровня, наличие этого API более высокого уровня может позволить другим инструментам получить доступ к этим данным.
Поверхностные данные кадров длинной анимации в DevTools
Вы можете отображать длинные кадры анимации в DevTools с помощью API performance.measure()
, которые затем отображаются на дорожке пользовательского времени DevTools в трассировках производительности, чтобы показать, на чем сосредоточить свои усилия для повышения производительности. Используя DevTools Extensibility API, их можно даже отобразить в отдельной дорожке:
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
performance.measure('LoAF', {
start: entry.startTime,
end: entry.startTime + entry.duration,
detail: {
devtools: {
dataType: "track-entry",
track: "Long animation frames",
trackGroup: "Performance Timeline",
color: "tertiary-dark",
tooltipText: 'LoAF'
}
}
});
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
В долгосрочной перспективе длинные кадры анимации, скорее всего, будут включены в сам DevTools, но предыдущий фрагмент кода позволяет пока разместить их там.
Первая запись на предыдущем рисунке также показывает, что браузер обрабатывает несколько задач одновременно в одном длинном кадре анимации, а не выполняет рендеринг между ними. Как упоминалось ранее, это может произойти, когда нет высокоприоритетных входных задач, но есть очередь задач. Первая длинная задача требует завершения некоторых обновлений рендеринга (в противном случае текущий длинный кадр анимации будет сброшен после нее, а новый начнется со следующей задачей), но вместо того, чтобы немедленно выполнить рендеринг, браузер обработал ряд дополнительные задачи и только затем выполнил задачу длительного рендеринга и завершил длинный кадр анимации. Это демонстрирует полезность просмотра длинных кадров анимации в DevTools, а не просто длинных задач, чтобы выявить задержку рендеринга.
Используйте данные длинных кадров анимации в других инструментах разработчика.
Расширение Web Vitals продемонстрировало ценность регистрации сводной отладочной информации для диагностики проблем с производительностью.
Теперь он также отображает данные длинных кадров анимации для каждого обратного вызова INP и каждого взаимодействия:
Используйте данные длинных кадров анимации в инструментах автоматического тестирования.
Аналогичным образом инструменты автоматического тестирования в конвейерах CI/CD могут выявить подробную информацию о потенциальных проблемах с производительностью, измеряя длинные кадры анимации во время выполнения различных наборов тестов.
Часто задаваемые вопросы
Некоторые из часто задаваемых вопросов по этому API включают в себя:
Почему бы просто не расширить или не усовершенствовать API длинных задач?
Это альтернативный взгляд на отчетность по схожим, но, в конечном счете, другим показателям потенциальных проблем с реагированием. Важно обеспечить, чтобы сайты, использующие существующий API длинных задач, продолжали функционировать, чтобы не нарушать существующие варианты использования.
Хотя API длинных задач может извлечь выгоду из некоторых функций LoAF (например, улучшенной модели атрибуции), мы считаем, что сосредоточение внимания на кадрах, а не на задачах, дает множество преимуществ, которые делают этот API фундаментально отличным от существующего API длинных задач.
Почему у меня нет записей сценария?
Это может указывать на то, что длинный кадр анимации возник не из-за JavaScipt, а из-за большой работы по рендерингу.
Это также может произойти, когда длинный кадр анимации вызван JavaScript, но атрибуция сценария не может быть предоставлена по различным причинам конфиденциальности, как отмечалось ранее (в первую очередь из-за того, что JavaScript не принадлежит странице).
Почему у меня есть записи сценария, но нет или ограничена исходная информация?
Это может произойти по ряду причин, в том числе из-за отсутствия хорошего источника, на который можно было бы указать .
Информация о скрипте также будет ограничена только sourceURL
(исключая любые перенаправления) для скриптов no-cors cross-origin
, с пустой строкой для sourceFunctionName
и -1
для sourceCharPosition
. Эту проблему можно решить, извлекая эти сценарии с помощью CORS, добавив crossOrigin = "anonymous"
к вызову <script>
.
Например, скрипт Диспетчера тегов Google по умолчанию, добавляемый на страницу:
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');</script>
<!-- End Google Tag Manager -->
Можно улучшить, добавив j.crossOrigin = "anonymous"
, чтобы обеспечить предоставление полной информации об авторстве для GTM.
Заменит ли это API длинных задач?
Хотя мы считаем, что API Long Animation Frames является лучшим и более полным API для измерения длинных задач, в настоящее время мы не планируем прекращать поддержку API Long Tasks.
Требуется обратная связь
Отзывы можно оставить в списке проблем GitHub , а ошибки в реализации API Chrome можно зарегистрировать в системе отслеживания проблем Chrome .
Заключение
API Long Animation Frames — это потрясающий новый API со многими потенциальными преимуществами по сравнению с предыдущим API Long Tasks.
Это оказывается ключевым инструментом для решения проблем реагирования, измеряемых INP. INP — это сложный показатель для оптимизации, и этот API — один из способов, с помощью которого команда Chrome стремится упростить выявление и решение проблем для разработчиков.
Однако сфера применения API Long Animation Frames выходит за рамки просто INP и может помочь выявить другие причины медленных обновлений, которые могут повлиять на общую плавность взаимодействия с пользователем веб-сайта.