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 не принадлежит странице).
Почему у меня есть записи сценария, но нет или ограничена исходная информация?
Это может произойти по ряду причин, в том числе из-за отсутствия хорошего источника, на который можно было бы указать .
Информация о скриптах также будет ограничена для скриптов no-cors cross-origin
, хотя это можно решить, извлекая эти скрипты с помощью 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 API (LOAF) API (LOAF) - это новый API, который стремится решить некоторые недостатки API длинных задач, позволяющих разработчикам получить более действенную информацию, чтобы помочь решить проблемы с отзывчивой и улучшить INP, а также получить представление о проблемах сглаживания. .
Хорошая отзывчивость означает, что страница быстро реагирует на взаимодействия, сделанные с ней. Это включает в себя возможность своевременно нарисовать любые обновления, необходимые пользователю, и избежать блокировки этих обновлений. Для INP рекомендуется ответить за 200 миллисекунд или менее , но для других обновлений (например, анимации) даже 200 миллисекунд могут быть слишком длинными.
Длинные анимационные рамки API - это альтернативный подход к измерению блокировки. Вместо того, чтобы измерять отдельные задачи , длинные анимационные кадры API - как предполагает его название - измерения длинных анимационных кадров . Длинная анимационная рамка - это когда обновление рендеринга задерживается выше 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 });
Предыдущие длинные анимационные кадры также могут быть запрошены с временной шкалы производительности, как SO:
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 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
: обработчик Platform Propect (например,fetch()
. Обратите внимание, что в случае обещаний все обработчики одинаковых обещаний смешаны как один «скрипт»).
-
reject-promise
: в соответствии сresolve-promise
, но для отказа. -
classic-script
: оценка скрипта (например,<script>
илиimport()
) -
module-script
: так же, как иclassic-script
, но для сценариев модулей.
-
- Отдельные данные времени для этого сценария:
-
startTime
: время функции входа была вызвана. -
duration
: Продолжительность междуstartTime
и когда последующая очередь микрозами завершена обработка. -
executionStart
: время после компиляции. -
forcedStyleAndLayoutDuration
: общее время, затрачиваемое на обработку принудительной макеты и стиля внутри этой функции (см. Thrashing ). -
pauseDuration
: общее время, проведенное в «приостановке» синхронных операций (Alert, Synchronous XHR).
-
- Подробности источника скрипта:
-
sourceURL
: имя ресурса сценария, где доступно (или пусто, если не найдено). -
sourceFunctionName
: имя функции скрипта, где доступно (или пусто, если не найдено). -
sourceCharPosition
: позиция символа сценария, где доступно (или -1, если не найдено).
-
-
windowAttribution
: контейнер (документ верхнего уровня или<iframe>
). Произошла длинная анимационная кадр. -
window
: Ссылка на то же Одионерское окно.
Там, где предоставлены, исходные записи позволяют разработчикам точно знать, как был вызван каждый сценарий в длинной анимационной рамке, вплоть до позиции символа в вызывающем скрипте. Это дает точное местоположение в ресурсе JavaScript, который привел к длинной анимационной кадре.
Пример входа производительности long-animation-frame
Полный пример ввода long-animation-frame
, содержащий один сценарий, IS:
{
"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 длинных анимационных кадров предназначено для использования в поле для сбора важных контекстуальных данных для взаимодействия пользователей, которые не смогли API длинных задач. Это может помочь вам выявить и воспроизвести проблемы с интерактивностью, которые вы, возможно, не обнаружили.
Обнаружение функций длинных анимационных кадров API -поддержки
Вы можете использовать следующий код для проверки, поддерживается ли API:
if (PerformanceObserver.supportedEntryTypes.includes('long-animation-frame')) {
// Monitor LoAFs
}
Ссылка на самое длинное взаимодействие с INP
Наиболее очевидный вариант использования для API API с длинными анимационными кадрами состоит в том, чтобы помочь диагностировать и исправить взаимодействие с следующей краской (INP) , и это была одна из ключевых причин, по которой команда Chrome разработала этот API. Хороший INP - это то, где все взаимодействия реагируются в 200 миллисекундах или меньше от взаимодействия до тех пор, пока рама не будет окрашена, и, поскольку API с длинными анимационными кадрами измеряют все кадры, которые занимают 50 мс или более, большинство проблемных INP должны включать данные Loaf, чтобы помочь вам диагностировать эти взаимодействия.
«Loaf INP» - это буханка, которая включает в себя взаимодействие INP, как показано на следующей диаграмме:
В некоторых случаях, когда INP -событие может охватывать два буханка - типично, если взаимодействие происходит после того, как кадр запустил часть предыдущего кадра, и поэтому обработчик событий обрабатывается в следующем кадре:
Возможно, даже в некоторых редких обстоятельствах он может охватывать более двух буханок.
Запись данных Loaf (ы), связанных с взаимодействием INP, позволяет вам получить гораздо больше информации о взаимодействии INP, чтобы помочь его диагностировать. Это особенно полезно для понимания задержки ввода : как вы можете видеть, какие другие сценарии работали в этом кадре.
Также может быть полезно понять необъяснимую продолжительность обработки и задержку презентации , если обработчики ваших событий не воспроизводят значения, которые можно использовать для тех, кто может работать с другими сценариями для ваших пользователей, которые могут не быть включены в ваше собственное тестирование.
Не существует прямых API для связи записи INP с соответствующей записью или записями Loaf , хотя это можно сделать в коде, сравнив время начала и окончания каждого (см. Пример примера Whynp ). Библиотека web-vitals
включает в себя все пересекающиеся буханки в свойство longAnimationFramesEntries
интерфейса атрибуции INP от V4.
После того, как вы связали запись или записи Loaf, вы можете включить информацию с атрибуцией INP. Объект scripts
содержит некоторую из наиболее ценной информации, поскольку он может показать, что еще работает в этих кадрах, чтобы макировать эти данные в вашу аналитическую службу позволит вам больше понять, почему взаимодействия были медленными.
Отчетность буханок для взаимодействия с INP - это хороший способ найти самые насущные проблемы интерактивности на вашей странице. Каждый пользователь может по -разному взаимодействовать с вашей страницей, и с достаточным объемом данных атрибуции INP ряд потенциальных проблем будут включены в данные атрибуции INP. Это позволяет вам сортировать сценарии по объему, чтобы увидеть, какие сценарии коррелируют с медленным INP.
Сообщите о более длинных данных анимации обратно в конечную точку аналитики
Одним из недостатков только взглядов на Loaf (ы) INP, вы можете пропустить другие потенциальные области для улучшений, которые могут вызвать будущие проблемы с INP. Это может привести к ощущению преследования вашего хвоста, где вы исправляете проблему INP, ожидая, что вы увидите огромное улучшение, только чтобы найти следующее самое медленное взаимодействие, на небольшое количество лучше, чем это, поэтому ваш INP не значительно улучшится.
Поэтому вместо того, чтобы смотреть только на буханку INP, вы можете рассмотреть все буханки в течение жизни страницы:
Тем не менее, каждая запись в буханке содержит значительные данные, поэтому вы, вероятно, захотите ограничить свой анализ только некоторыми хлебными буханками. Кроме того, поскольку длинные записи рамки анимации могут быть довольно большими, разработчики должны решить, какие данные из записи следует отправлять в аналитику. Например, сводное время записи и, возможно, имена сценариев, или какой -либо другой минимальный набор других контекстуальных данных, которые могут считаться необходимыми.
Некоторые предложенные шаблоны, чтобы уменьшить количество данных о длинных анимационных кадрах, включают в себя:
- Наблюдайте за длинными анимационными рамами с взаимодействиями
- Наблюдайте за длинными анимационными рамами с высокой продолжительностью блокировки
- Наблюдайте за длинными анимационными кадрами во время критических обновлений пользовательского интерфейса, чтобы улучшить плавность
- Наблюдайте за худшими длинными анимационными кадрами
- Определить общие закономерности в длинных анимационных кадрах
Какой из этих моделей работает лучше всего для вас, зависит от того, насколько далеко вы находитесь в вашем пути оптимизации и насколько распространены длинные анимационные кадры. Для сайта, который никогда не оптимизировался для отзывчивости, может быть много буханок, вы можете ограничить просто буханки с взаимодействиями, или установить высокий порог, или только взглянуть на худшие.
Когда вы решаете свои общие проблемы с отзывчивой, вы можете расширить это, не ограничивая только взаимодействие или высокую продолжительность блокировки или путем снижения порогов.
Наблюдайте за длинными анимационными рамами с взаимодействиями
Чтобы получить представление за пределы только в INP длинной анимационной рамке, вы можете посмотреть на все буханки с взаимодействиями (которые могут быть обнаружены в результате наличия значения firstUIEventTimestamp
) с высокой blockingDuration
.
Это также может быть более простым методом мониторинга хлебов INP, а не пытаться коррелировать эти два, которые могут быть более сложными. В большинстве случаев это будет включать в себя LOAF INP для данного визита, и в редких случаях, когда это не все еще вызывает длительные взаимодействия, которые важны для исправления, поскольку они могут быть взаимодействием INP для других пользователей.
Следующие кодовые журналы вводит все записи буханки с blockingDuration
более 100 миллисекунд, где взаимодействие происходило во время кадра. 100 выбирается здесь, потому что это меньше, чем 200 миллисекундных «хороших» порога INP. Вы можете выбрать более высокое или более низкое значение в зависимости от ваших потребностей.
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, если пользователь взаимодействует во время этих длинных анимационных кадров.
Следующие журналы кода вводат все записи буханки с продолжительностью блокировки более 100 миллисекунд, где взаимодействие происходило во время кадра. 100 выбирается здесь, потому что он меньше, чем 200 миллисекунд «Хороший» порог INP, чтобы помочь определить потенциальные проблемы с проблемами, сохраняя при этом количество длинных анимационных кадров, сообщаемых минимумом. Вы можете выбрать более высокое или более низкое значение в зависимости от ваших потребностей.
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 худших буханок, с взаимодействиями, более 100 миллисекунд.
В соответствующее время ( в идеале на событии visibilitychange
) Beacon обратно в аналитику. Для локального тестирования вы можете использовать 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 пользователя в трассировках производительности, чтобы показать, где сосредоточить свои усилия для улучшения производительности. Используя API расширяемости Devtools, это может быть даже показано в их собственном пути:
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 не принадлежит странице).
Зачем у меня есть записи сценария, но нет или ограниченная информация источника?
Это может произойти по ряду причин, в том числе не является хорошим источником .
Информация о скрипте также будет ограничена для сценариев no-cors cross-origin
, хотя это может быть разрешено путем извлечения этих сценариев с использованием CORS путем добавления crossOrigin = "anonymous"
к вызову <script>
.
Например, сценарий по умолчанию Google Tag Manager, чтобы добавить на страницу:
<!-- 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 длинных анимационных кадров является лучшим, более полным API для измерения длинных задач, в настоящее время нет планов по контролю за длинными задачами API.
Обратная связь разыскивается
Обратная связь может быть предоставлена в списке проблем GitHub , или ошибки в реализации Chrome API могут быть поданы в трекер выпуска Chrome .
Заключение
API длинных анимационных кадров - это захватывающий новый API со многими потенциальными преимуществами по сравнению с предыдущими длинными задачами API.
Это оказывается ключевым инструментом для решения проблем реагирования, измеренных INP. INP является сложной метрикой для оптимизации, и этот API является одним из способов, которым команда Chrome стремится упростить идентификацию и решение проблем для разработчиков.
Область API API длинных анимационных кадров выходит за рамки Just INP, и он может помочь определить другие причины медленных обновлений, которые могут повлиять на общую плавность пользовательского опыта веб -сайта.
Благодарности
Миниатюрный образ Генри Будь в UNSPLASH .
,API API с длинными анимационными кадрами (LO-AF-AF)-это обновление для API длинных задач, чтобы обеспечить лучшее понимание обновлений медленного пользовательского интерфейса (UI). Это может быть полезно для выявления медленных анимационных кадров, которые, вероятно, повлияют на взаимодействие с помощью основной веб -метрики, которая измеряет отзывчивость, или идентифицирует другой брюк, который влияет на гладкость .
Статус API
После исследования происхождения от Chrome 116 до Chrome 122 , API Loaf отправился из Chrome 123 .
Фон: API длинных задач
API длинных анимационных кадров - это альтернатива длинным задачам API, который был доступен в 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
Измерение длинных задач в полевых условиях с использованием наблюдателя производительности только несколько полезно. На самом деле это не дает столько информации, помимо того, что произошла долгая задача, и сколько времени это заняло.
Инструменты реального мониторинга пользователей (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 API (LOAF) API (LOAF) - это новый API, который стремится решить некоторые недостатки API длинных задач, позволяющих разработчикам получить более действенную информацию, чтобы помочь решить проблемы с отзывчивой и улучшить INP, а также получить представление о проблемах сглаживания. .
Хорошая отзывчивость означает, что страница быстро реагирует на взаимодействия, сделанные с ней. Это включает в себя возможность своевременно нарисовать любые обновления, необходимые пользователю, и избежать блокировки этих обновлений. Для INP рекомендуется ответить за 200 миллисекунд или менее , но для других обновлений (например, анимации) даже 200 миллисекунд могут быть слишком длинными.
Длинные анимационные рамки API - это альтернативный подход к измерению блокировки. Вместо того, чтобы измерять отдельные задачи , длинные анимационные кадры API - как предполагает его название - измерения длинных анимационных кадров . Длинная анимационная рамка - это когда обновление рендеринга задерживается выше 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 });
Предыдущие длинные анимационные кадры также могут быть запрошены с временной шкалы производительности, как SO:
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
: the time of the first UI event (mouse/keyboard and so on) to be handled during the course of this frame. -
blockingDuration
: the total duration in milliseconds for which the animation frame would block processing of input or other high priority tasks.
An explanation of blockingDuration
A long animation frame may be made up of a number of tasks. The blockingDuration
is the sum of task durations longer than 50 milliseconds (including the final render duration within the longest task).
For example, if a long animation frame was made up of two tasks of 55 milliseconds and 65 milliseconds followed by a render of 20 milliseconds, then the duration
would be approximately 140 milliseconds with a blockingDuration
of (55 - 50) + (65 + 20 - 50) = 40 milliseconds. For 40 milliseconds during this 140 millisecond long animation frame, the frame was considered blocked from handling input.
Whether to look at duration
or blockingDuration
For the common 60 hertz display, a browser will try to schedule a frame at least every 16.66 milliseconds (to ensure smooth updates), or after a high priority task like input handling (to ensure responsive updates). However, if there is no input—nor other high-priority tasks—but there is a queue of other tasks, the browser will typically continue the current frame well past 16.66 milliseconds no matter how well broken up the tasks are within it. That is, the browser will always try to prioritise inputs, but may choose to tackle a queue of tasks over render updates. This is due to rendering being an expensive process so processing a combined rendering task for multiple tasks usually leads to an overall reduction of work.
Therefore, long animation frames with a low or zero blockingDuration
should still feel responsive to input. Reducing or eliminating blockingDuration
by breaking up long tasks is therefore key to improving responsiveness as measured by INP.
However, a lot of long animation frames, regardless of blockingDuration
indicates UI updates that are delayed and so can still affect smoothness and lead to a feeling of a laggy user interface for scrolling or animations, even if these are less of an issue for responsiveness as measured by INP. To understand issues in this area look at the duration
, but these can be more tricky to optimize for since you cannot solve this by breaking up work, but instead must reduce work.
Frame timings
The previously mentioned timestamps allow the long animation frame to be divided into timings:
Тайминг | Расчет |
---|---|
Время начала | startTime |
Конец Времени | startTime + duration |
Work duration | renderStart ? renderStart - startTime : duration |
Render duration | renderStart ? (startTime + duration) - renderStart: 0 |
Render: Pre-layout duration | styleAndLayoutStart ? styleAndLayoutStart - renderStart : 0 |
Render: Style and Layout duration | styleAndLayoutStart ? (startTime + duration) - styleAndLayoutStart : 0 |
Better script attribution
The long-animation-frame
entry type includes better attribution data of each script that contributed to a long animation frame (for scripts longer than 5 milliseconds).
Similar to the Long Tasks API, this will be provided in an array of attribution entries, each of which details:
- A
name
andEntryType
both will returnscript
. - A meaningful
invoker
, indicating how the script was called (for example,'IMG#id.onload'
,'Window.requestAnimationFrame'
, or'Response.json.then'
). - The
invokerType
of the script entry point:-
user-callback
: A known callback registered from a web platform API (for example,setTimeout
,requestAnimationFrame
). -
event-listener
: A listener to a platform event (for example,click
,load
,keyup
). -
resolve-promise
: Handler of a platform promise (for example,fetch()
. Note that in the case of promises, all the handlers of the same promises are mixed together as one "script").
-
reject-promise
: As perresolve-promise
, but for the rejection. -
classic-script
: Script evaluation (for example,<script>
orimport()
) -
module-script
: Same asclassic-script
, but for module scripts.
-
- Separate timing data for that script:
-
startTime
: Time the entry function was invoked. -
duration
: The duration betweenstartTime
and when the subsequent microtask queue has finished processing. -
executionStart
: The time after compilation. -
forcedStyleAndLayoutDuration
: The total time spent processing forced layout and style inside this function (see thrashing ). -
pauseDuration
: Total time spent in "pausing" synchronous operations (alert, synchronous XHR).
-
- Script source details:
-
sourceURL
: The script resource name where available (or empty if not found). -
sourceFunctionName
: The script function name where available (or empty if not found). -
sourceCharPosition
: The script character position where available (or -1 if not found).
-
-
windowAttribution
: The container (the top-level document, or an<iframe>
) the long animation frame occurred in. -
window
: A reference to the same-origin window.
Where provided, the source entries allows developers to know exactly how each script in the long animation frame was called, down to the character position in the calling script. This gives the exact location in a JavaScript resource that resulted in the long animation frame.
Example of a long-animation-frame
performance entry
A complete long-animation-frame
performance entry example, containing a single script, is:
{
"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
}
As can be seen, this gives an unprecedented amount of data for websites to be able to understand the cause of laggy rendering updates.
Use the Long Animation Frames API in the field
Tools like Chrome DevTools and Lighthouse—while useful for discovering and reproducing issues—are lab tools that may miss important aspects of the user experience that only field data can provide.
The Long Animation Frames API is designed to be used in the field to gather important contextual data for user interactions that the Long Tasks API couldn't. This can help you to identify and reproduce issues with interactivity that you might not have otherwise discovered.
Feature detecting Long Animation Frames API support
You can use the following code to test if the API is supported:
if (PerformanceObserver.supportedEntryTypes.includes('long-animation-frame')) {
// Monitor LoAFs
}
Link to the longest INP interaction
The most obvious use case for the Long Animation Frames API is to to help diagnose and fix Interaction to Next Paint (INP) issues, and that was one of the key reasons the Chrome team developed this API. A good INP is where all interactions are responded to in 200 milliseconds or less from interaction until the frame is painted, and since the Long Animation Frames API measures all frames that take 50ms or more, most problematic INPs should include LoAF data to help you diagnose those interactions.
The "INP LoAF" is the LoAF which includes the INP interaction, as shown in the following diagram:
In some cases it's possible for an INP event to span two LoAFs—typically if the interaction happens after the frame has started the rendering part of the previous frame, and so the event handler is processed in the next frame:
It's even possible that it may span more than two LoAFs in some rare circumstances.
Recording the LoAF(s) data associated with the INP interaction lets you to get much more information about the INP interaction to help diagnose it. This is particularly helpful to understand input delay : as you can see what other scripts were running in that frame.
It can also be helpful to understand unexplained processing duration and presentation delay if your event handlers are not reproducing the values seen for those as other scripts may be running for your users which may not be included in your own testing.
There is no direct API to link an INP entry with its related LoAF entry or entries , though it is possible to do so in code by comparing the start and end times of each (see the WhyNp example script ). The web-vitals
library includes all intersecting LoAFs in the longAnimationFramesEntries
property of the INP attribution interface from v4.
Once you have linked the LoAF entry or entries, you can include information with INP attribution. The scripts
object contains some of the most valuable information as it can show what else was running in those frames so beaconing back that data to your analytics service will allow you to understand more about why interactions were slow.
Reporting LoAFs for the INP interaction is a good way to find the what is the most pressing interactivity issues on your page. Each user may interact differently with your page and with enough volume of INP attribution data, a number of potential issues will be included in INP attribution data. This lets you to sort scripts by volume to see which scripts are correlating with slow INP.
Report more long animation data back to an analytics endpoint
One downside to only looking at the INP LoAF(s), is you may miss other potential areas for improvements that may cause future INP issues. This can lead to a feeling of chasing your tail where you fix an INP issue expecting to see a huge improvement, only to find the next slowest interaction is only a small amount better than that so your INP doesn't improve much.
So rather than only looking at the INP LoAF, you may want to consider all LoAFs across the page lifetime:
However, each LoAF entry contains considerable data, so you will likely want to restrict your analysis to only some LoAFs. Additionally, as the long animation frame entries can be quite large, developers should decide what data from the entry should be sent to analytics. For example, the summary times of the entry and perhaps the script names, or some other minimum set of other contextual data that may be deemed necessary.
Some suggested patterns to reduce the amount of long animation frame data includes:
- Observe long animation frames with interactions
- Observe long animation frames with high blocking durations
- Observe long animation frames during critical UI updates to improve smoothness
- Observe the worst long animation frames
- Identify common patterns in long animation frames
Which of these patterns works best for you, depends on how far along your optimization journey you are, and how common long animation frames are. For a site that has never optimized for responsiveness before, there may be many LoAFs do you may want to limit to just LoAFs with interactions, or set a high threshold, or only look at the worst ones.
As you resolve your common responsiveness issues, you may expand this by not limiting to just interactions or high blocking durations or by lowering thresholds.
Observe long animation frames with interactions
To gain insights beyond just the INP long animation frame, you can look at all LoAFs with interactions (which can be detected by the presence of a firstUIEventTimestamp
value) with a high blockingDuration
.
This can also be an easier method of monitoring INP LoAFs rather than trying to correlate the two, which can be more complex. In most cases this will include the INP LoAF for a given visit, and in rare cases when it doesn't it still surfaces long interactions that are important to fix, as they may be the INP interaction for other users.
The following code logs all LoAF entries with a blockingDuration
greater than 100 milliseconds where an interaction occurred during the frame. The 100 is chosen here because it is less than the 200 millisecond "good" INP threshold. You could choose a higher or lower value depending on your needs.
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 });
Observe long animation frames with high blocking durations
As an improvement to looking at all long animation frames with interactions, you may want to look at all long animation frames with high blocking durations. These indicate potential INP problems if a user does interact during these long animation frames.
The following code logs all LoAF entries with a blocking duration greater than 100 milliseconds where an interaction occurred during the frame. The 100 is chosen here because it is less than the 200 millisecond "good" INP threshold to help identify potential problem frames, while keeping the amount of long animation frames reported to a minimum. You could choose a higher or lower value depending on your needs.
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 });
Observe long animation frames during critical UI updates to improve smoothness
As mentioned previously, looking at high blocking duration long animation frames can help to address input responsiveness. But for smoothness you should look at all long animation frames with a long duration
.
Since, this can get quite noisy you may want to restrict measurements of these to key points with a pattern like this:
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;
}
Observe the worst long animation frames
Rather than having a set threshold, sites may want to collect data on the longest animation frame (or frames), to reduce the volume of data that needs to be beaconed. So no matter how many long animation frames a page experiences, only data for the worst on, five, ten, or however many long animation frames absolutely necessary is beaconed back.
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 });
These strategies can also be combined—only look at the 10 worst LoAFs, with interactions, longer than 100 milliseconds.
At the appropriate time ( ideally on the visibilitychange
event ) beacon back to analytics. For local testing you can use console.table
periodically:
console.table(longestBlockingLoAFs);
Identify common patterns in long animation frames
An alternative strategy would be to look at common scripts appearing the most in long animation frame entries. Data could be reported back at a script and character position level to identify repeat offenders.
This may work particularly well for customizable platforms where themes or plugins causing performance issues could be identified across a number of sites.
The execution time of common scripts—or third-party origins—in long animation frames could be summed up and reported back to identify common contributors to long animation frames across a site or a collection of sites. For example to look at URLs:
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});
And example of this output is:
(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 |
Use the Long Animation Frames API in tooling
The API also allows additional developer tooling for local debugging. While some tooling like Lighthouse and Chrome DevTools have been able to gather much of this data using lower-level tracing details, having this higher-level API could allow other tools to access this data.
Surface long animation frames data in DevTools
You can surface long animation frames in DevTools using the performance.measure()
API, which are then displayed in the DevTools user timings track in performance traces to show where to focus your efforts for performance improvements. Using the DevTools Extensibility API these can even be shown in their own track:
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 });
Longer term, long animation frames will likely be incorporated into DevTools itself, but the previous code snippet allows it to be surfaced there in the meantime.
The first entry in the previous figure also demonstrates where the browser has processed several tasks together in the same long animation frame rather than render between them. As mentioned previously this can happen when there are no high-priority input tasks, but there is a queue of tasks. The first long task has some render updates to complete (otherwise the current long animation frame would be reset after it, and a new one would start with the next task), but instead of actioning that render immediately, the browser has processed a number of additional tasks and only then actioned the long render task and ended the long animation frame. This demonstrates the usefulness of looking at long animation frames in DevTools, rather than just long tasks, to help identify delayed renders.
Use long animation frames data in other developer tooling
The Web Vitals extension has shown the value in logging summary debug information to diagnose performance issues.
It now also surfaces long animation frame data for each INP callback and each interaction:
Use long animation frames data in automated testing tools
Similarly automated testing tools in CI/CD pipelines can surface details on potential performance issues by measuring long animation frames while running various test suites.
Часто задаваемые вопросы
Some of the frequently asked questions on this API include:
Why not just extend or iterate on the Long Tasks API?
This is an alternative look at reporting a similar—but ultimately different—measurement of potential responsiveness issues. It's important to ensure sites relying on the existing Long Tasks API continue to function to avoid disrupting existing use cases.
While the Long Tasks API may benefit from some of the features of LoAF (such as a better attribution model), we believe that focusing on frames rather than tasks offers many benefits that make this a fundamentally different API to the existing Long Tasks API.
Why do I not have script entries?
This may indicate that the long animation frame was not due to JavaScipt, but instead due to large render work.
This can also happen when the long animation frame is due to JavaScript but where the script attribution cannot be provided for various privacy reasons as noted previously (primarily that JavaScript not owned by the page).
Why do I have script entries but no, or limited, source information?
This can happen for a number of reasons, including there not being a good source to point to .
Script information will also be limited for no-cors cross-origin
scripts, though this can be resolved by fetching those scripts using CORS by adding crossOrigin = "anonymous"
to the <script>
call.
For example, the default Google Tag Manager script to add to the page:
<!-- 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 -->
Can be enhanced to add j.crossOrigin = "anonymous"
to allow full attribution details to be provided for GTM
Will this replace the Long Tasks API?
While we believe the Long Animation Frames API is a better, more complete API for measuring long tasks, at this time, there are no plans to deprecate the Long Tasks API.
Feedback wanted
Feedback can be provided at the GitHub Issues list , or bugs in Chrome's implementation of the API can be filed in Chrome's issue tracker .
Заключение
The Long Animation Frames API is an exciting new API with many potential advantages over the previous Long Tasks API.
It is proving to be a key tool for addressing responsiveness issues as measured by INP. INP is a challenging metric to optimize and this API is one way the Chrome team is seeking to make identifying and addressing issues easier for developers.
The scope of the Long Animation Frames API extends beyond just INP though, and it can help identify other causes of slow updates which can affect the overall smoothness of a website's user experience.