Очень важно иметь ориентированные на пользователя показатели , которые можно измерить повсеместно на любом веб-сайте. Эти показатели позволяют:
- Поймите, как реальные пользователи воспринимают Интернет в целом.
- Сравните свой сайт с сайтом конкурента.
- Отслеживайте полезные и полезные данные в своих аналитических инструментах без необходимости писать собственный код.
Универсальные метрики предлагают хорошую основу, но во многих случаях вам нужно измерять больше , чем просто эти метрики, чтобы получить полную информацию о вашем конкретном сайте.
Специальные метрики позволяют измерять аспекты взаимодействия с вашим сайтом, которые могут относиться только к нему, например:
- Сколько времени требуется одностраничному приложению (SPA) для перехода с одной «страницы» на другую.
- Сколько времени требуется странице для отображения данных, полученных из базы данных для вошедших в систему пользователей.
- Сколько времени требуется приложению, отображаемому на стороне сервера (SSR), для гидратации .
- Коэффициент попадания в кеш ресурсов, загруженных вернувшимися посетителями.
- Задержка событий щелчка или клавиатуры в игре.
API для измерения пользовательских метрик
Исторически у веб-разработчиков не было большого количества низкоуровневых API для измерения производительности, и в результате им приходилось прибегать к хакам, чтобы оценить, насколько хорошо работает сайт.
Например, можно определить, заблокирован ли основной поток из-за длительного выполнения задач JavaScript, запустив цикл requestAnimationFrame
и вычислив разницу между каждым кадром. Если разница значительно превышает частоту кадров дисплея, вы можете сообщить об этом как о длительной задаче. Однако такие хаки не рекомендуются, поскольку они сами по себе влияют на производительность (например, разряжая батарею).
Первое правило эффективного измерения производительности — убедиться, что ваши методы измерения производительности сами по себе не вызывают проблем с производительностью. Поэтому для любых специальных показателей, которые вы измеряете на своем сайте, лучше всего использовать один из следующих API, если это возможно.
API наблюдателя за производительностью
API Performance Observer — это механизм, который собирает и отображает данные из всех других API производительности, обсуждаемых на этой странице. Понимание этого имеет решающее значение для получения качественных данных.
Вы можете использовать PerformanceObserver
для пассивной подписки на события, связанные с производительностью. Это позволяет обратным вызовам API запускаться в периоды простоя , что означает, что они обычно не влияют на производительность страницы.
Чтобы создать PerformanceObserver
, передайте ему обратный вызов, который будет запускаться при отправке новых записей о производительности. Затем вы сообщаете наблюдателю, какие типы записей следует прослушивать, используя метод observe()
:
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
po.observe({type: 'some-entry-type'});
В следующих разделах перечислены все различные типы записей, доступные для наблюдения, но в новых браузерах вы можете проверить, какие типы записей доступны, с помощью статического свойства PerformanceObserver.supportedEntryTypes
.
Обратите внимание на записи, которые уже произошли
По умолчанию объекты PerformanceObserver
могут наблюдать записи только по мере их появления. Это может вызвать проблемы, если вы хотите отложенно загружать код аналитики производительности, чтобы он не блокировал ресурсы с более высоким приоритетом.
Чтобы получить исторические записи (после того, как они произошли), установите для флага buffered
значение true
при вызове observe()
. Браузер будет включать исторические записи из своего буфера записи производительности при первом вызове обратного вызова PerformanceObserver
, вплоть до максимального размера буфера для этого типа .
po.observe({
type: 'some-entry-type',
buffered: true,
});
Устаревшие API производительности, которых следует избегать
До API Performance Observer разработчики могли получать доступ к записям производительности, используя следующие три метода, определенные в объекте performance
:
Хотя эти API по-прежнему поддерживаются, их использование не рекомендуется, поскольку они не позволяют отслеживать появление новых записей. Кроме того, многие новые API (например, largest-contentful-paint
) не предоставляются через объект performance
, они предоставляются только через PerformanceObserver
.
Если вам не нужна совместимость с Internet Explorer, лучше избегать этих методов в своем коде и в дальнейшем использовать PerformanceObserver
.
API синхронизации пользователя
API пользовательского времени — это API общего назначения для измерения метрик, основанных на времени. Он позволяет произвольно отмечать точки во времени, а затем позже измерять продолжительность между этими отметками.
// Record the time immediately before running a task.
performance.mark('myTask:start');
await doMyTask();
// Record the time immediately after running a task.
performance.mark('myTask:end');
// Measure the delta between the start and end of the task
performance.measure('myTask', 'myTask:start', 'myTask:end');
Хотя такие API, как Date.now()
или performance.now()
предоставляют аналогичные возможности, преимущество использования User Timing API заключается в том, что он хорошо интегрируется с инструментами повышения производительности. Например, Chrome DevTools визуализирует измерения пользовательского времени на панели «Производительность» , а многие поставщики аналитики также автоматически отслеживают любые ваши измерения и отправляют данные о продолжительности на свой аналитический сервер.
Чтобы сообщить об измерениях пользовательского времени, вы можете использовать PerformanceObserver и зарегистрироваться для наблюдения за записями типа measure
:
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `measure` entries to be dispatched.
po.observe({type: 'measure', buffered: true});
API длинных задач
API длинных задач полезен для определения того, когда основной поток браузера блокируется на достаточно долгое время, чтобы повлиять на частоту кадров или задержку ввода. API будет сообщать обо всех задачах, которые выполняются дольше 50 миллисекунд.
Каждый раз, когда вам нужно запустить дорогостоящий код или загрузить и выполнить большие сценарии, полезно отслеживать, не блокирует ли этот код основной поток. Фактически, многие метрики более высокого уровня создаются поверх самого API длинных задач (например, время взаимодействия (TTI) и общее время блокировки (TBT) ).
Чтобы определить, когда выполняются длительные задачи, вы можете использовать PerformanceObserver и зарегистрироваться для наблюдения за записями типа longtask
:
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `longtask` entries to be dispatched.
po.observe({type: 'longtask', buffered: true});
API длинных кадров анимации
API длинных кадров анимации — это новая версия API длинных задач, которая рассматривает длинные кадры , а не длинные задачи , длительностью более 50 миллисекунд. Это устраняет некоторые недостатки API длинных задач , в том числе лучшую атрибуцию и более широкий спектр потенциально проблемных задержек.
Чтобы определить, когда происходят длинные кадры, вы можете использовать PerformanceObserver и зарегистрироваться для наблюдения за записями типа long-animation-frame
:
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `long-animation-frame` entries to be dispatched.
po.observe({type: 'long-animation-frame', buffered: true});
API синхронизации элементов
Метрика «Наибольшая отрисовка содержимого» (LCP) полезна для определения того, когда на экране было нарисовано самое большое изображение или текстовый блок, но в некоторых случаях необходимо измерить время рендеринга другого элемента.
В этих случаях используйте Element Timing API . API LCP фактически построен на основе API синхронизации элементов и добавляет автоматические отчеты о самом большом элементе с содержимым, но вы также можете создавать отчеты о других элементах, явно добавляя к ним атрибут elementtiming
и регистрируя PerformanceObserver для наблюдения за типом записи element
.
<img elementtiming="hero-image" />
<p elementtiming="important-paragraph">This is text I care about.</p>
<!-- ... -->
<script>
const po = new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `element` entries to be dispatched.
po.observe({type: 'element', buffered: true});
</script>
API синхронизации событий
Метрика «Взаимодействие с следующей отрисовкой» (INP) оценивает общую скорость реагирования страницы, наблюдая за всеми взаимодействиями с щелчками, касаниями и клавиатурой на протяжении всего существования страницы. INP страницы чаще всего представляет собой взаимодействие, выполнение которого заняло больше всего времени: с момента, когда пользователь инициировал взаимодействие, до момента, когда браузер рисует следующий кадр, показывающий визуальный результат ввода пользователя.
Метрика INP стала возможной благодаря API синхронизации событий . Этот API предоставляет ряд временных меток, которые возникают в течение жизненного цикла события, в том числе:
-
startTime
: время, когда браузер получает событие. -
processingStart
: время, когда браузер может начать обработку обработчиков события. -
processingEnd
: время, когда браузер завершает выполнение всего синхронного кода, инициированного обработчиками событий для этого события. -
duration
: время (округленное до 8 миллисекунд по соображениям безопасности) между получением браузером события и возможностью отрисовки следующего кадра после завершения выполнения всего синхронного кода, инициированного обработчиками событий.
В следующем примере показано, как использовать эти значения для создания пользовательских измерений:
const po = new PerformanceObserver((entryList) => {
// Get the last interaction observed:
const entries = Array.from(entryList.getEntries()).forEach((entry) => {
// Get various bits of interaction data:
const inputDelay = entry.processingStart - entry.startTime;
const processingTime = entry.processingEnd - entry.processingStart;
const presentationDelay = entry.startTime + entry.duration - entry.processingEnd;
const duration = entry.duration;
const eventType = entry.name;
const target = entry.target || "(not set)"
console.log("----- INTERACTION -----");
console.log(`Input delay (ms): ${inputDelay}`);
console.log(`Event handler processing time (ms): ${processingTime}`);
console.log(`Presentation delay (ms): ${presentationDelay}`);
console.log(`Total event duration (ms): ${duration}`);
console.log(`Event type: ${eventType}`);
console.log(target);
});
});
// A durationThreshold of 16ms is necessary to include more
// interactions, since the default is 104ms. The minimum
// durationThreshold is 16ms.
po.observe({type: 'event', buffered: true, durationThreshold: 16});
API синхронизации ресурсов
API синхронизации ресурсов дает разработчикам подробную информацию о том, как были загружены ресурсы для конкретной страницы. Несмотря на название API, предоставляемая им информация не ограничивается только данными о времени (хотя их достаточно ). Другие данные, к которым вы можете получить доступ, включают:
-
initiatorType
: как был получен ресурс: например, из тега<script>
или<link>
или из вызоваfetch()
. -
nextHopProtocol
: протокол, используемый для получения ресурса, напримерh2
илиquic
. -
encodedBodySize
/ decodedBodySize ]: размер ресурса в закодированном или декодированном виде (соответственно) -
transferSize
: размер ресурса, который был фактически передан по сети. Когда ресурсы заполняются кешем, это значение может быть намного меньше, чемencodedBodySize
, а в некоторых случаях оно может быть нулевым (если повторная проверка кеша не требуется).
Вы можете использовать свойство transferSize
записей времени ресурсов для измерения показателя частоты попадания в кэш или показателя общего размера кэшированного ресурса , что может быть полезно для понимания того, как ваша стратегия кэширования ресурсов влияет на производительность для повторных посетителей.
В следующем примере регистрируются все ресурсы, запрошенные страницей, и указывается, был ли каждый ресурс обработан кэшем.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// If transferSize is 0, the resource was fulfilled using the cache.
console.log(entry.name, entry.transferSize === 0);
}
});
// Start listening for `resource` entries to be dispatched.
po.observe({type: 'resource', buffered: true});
API синхронизации навигации
API синхронизации навигации аналогичен API синхронизации ресурсов, но он сообщает только о запросах навигации . Тип записи navigation
также аналогичен типу записи resource
, но он содержит некоторую дополнительную информацию , специфичную только для запросов навигации (например, при возникновении событий DOMContentLoaded
и load
).
Одна метрика, которую многие разработчики отслеживают, чтобы понять время ответа сервера ( время до первого байта (TTFB) ), доступна с помощью API синхронизации навигации — в частности, это метка времени responseStart
записи.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// If transferSize is 0, the resource was fulfilled using the cache.
console.log('Time to first byte', entry.responseStart);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});
Еще одна метрика, которая может волновать разработчиков, использующих Service Worker, — это время запуска Service Worker для запросов навигации. Это количество времени, которое требуется браузеру для запуска рабочего потока службы, прежде чем он сможет начать перехватывать события выборки.
Время запуска сервис-воркера для конкретного запроса навигации можно определить по разнице между entry.responseStart
и entry.workerStart
.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Service Worker startup time:',
entry.responseStart - entry.workerStart);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});
API синхронизации сервера
API синхронизации сервера позволяет передавать данные о времени конкретного запроса с вашего сервера в браузер через заголовки ответов. Например, вы можете указать, сколько времени потребовалось для поиска данных в базе данных для определенного запроса, что может быть полезно при отладке проблем с производительностью, вызванных медленной работой сервера.
Для разработчиков, которые используют сторонних поставщиков аналитики, API синхронизации сервера — единственный способ сопоставить данные о производительности сервера с другими бизнес-показателями, которые могут измеряться этими инструментами аналитики.
Чтобы указать данные синхронизации сервера в ваших ответах, вы можете использовать заголовок ответа Server-Timing
. Вот пример.
HTTP/1.1 200 OK
Server-Timing: miss, db;dur=53, app;dur=47.2
Затем со своих страниц вы можете прочитать эти данные как о resource
, так и о записях navigation
из API-интерфейсов синхронизации ресурсов и времени навигации.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Logs all server timing data for this response
console.log('Server Timing', entry.serverTiming);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});
Очень важно иметь ориентированные на пользователя показатели , которые можно измерить повсеместно на любом веб-сайте. Эти показатели позволяют:
- Поймите, как реальные пользователи воспринимают Интернет в целом.
- Сравните свой сайт с сайтом конкурента.
- Отслеживайте полезные и полезные данные в своих аналитических инструментах без необходимости писать собственный код.
Универсальные метрики предлагают хорошую основу, но во многих случаях вам нужно измерять больше , чем просто эти метрики, чтобы получить полную информацию о вашем конкретном сайте.
Специальные метрики позволяют измерять аспекты взаимодействия с вашим сайтом, которые могут относиться только к нему, например:
- Сколько времени требуется одностраничному приложению (SPA) для перехода с одной «страницы» на другую.
- Сколько времени требуется странице для отображения данных, полученных из базы данных для вошедших в систему пользователей.
- Сколько времени требуется приложению, отображаемому на стороне сервера (SSR), для гидратации .
- Коэффициент попадания в кеш ресурсов, загруженных вернувшимися посетителями.
- Задержка событий щелчка или клавиатуры в игре.
API для измерения пользовательских метрик
Исторически у веб-разработчиков не было большого количества низкоуровневых API для измерения производительности, и в результате им приходилось прибегать к хакам, чтобы оценить, насколько хорошо работает сайт.
Например, можно определить, заблокирован ли основной поток из-за длительного выполнения задач JavaScript, запустив цикл requestAnimationFrame
и вычислив разницу между каждым кадром. Если разница значительно превышает частоту кадров дисплея, вы можете сообщить об этом как о длительной задаче. Однако такие хаки не рекомендуются, поскольку они сами по себе влияют на производительность (например, разряжая батарею).
Первое правило эффективного измерения производительности — убедиться, что ваши методы измерения производительности сами по себе не вызывают проблем с производительностью. Поэтому для любых специальных показателей, которые вы измеряете на своем сайте, лучше всего использовать один из следующих API, если это возможно.
API наблюдателя за производительностью
API Performance Observer — это механизм, который собирает и отображает данные из всех других API производительности, обсуждаемых на этой странице. Понимание этого имеет решающее значение для получения качественных данных.
Вы можете использовать PerformanceObserver
для пассивной подписки на события, связанные с производительностью. Это позволяет обратным вызовам API запускаться в периоды простоя , что означает, что они обычно не влияют на производительность страницы.
Чтобы создать PerformanceObserver
, передайте ему обратный вызов, который будет запускаться при отправке новых записей о производительности. Затем вы сообщаете наблюдателю, какие типы записей следует прослушивать, используя метод observe()
:
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
po.observe({type: 'some-entry-type'});
В следующих разделах перечислены все различные типы записей, доступные для наблюдения, но в новых браузерах вы можете проверить, какие типы записей доступны, с помощью статического свойства PerformanceObserver.supportedEntryTypes
.
Обратите внимание на записи, которые уже произошли
По умолчанию объекты PerformanceObserver
могут наблюдать записи только по мере их появления. Это может вызвать проблемы, если вы хотите отложенно загружать код аналитики производительности, чтобы он не блокировал ресурсы с более высоким приоритетом.
Чтобы получить исторические записи (после того, как они произошли), установите для флага buffered
значение true
при вызове observe()
. Браузер будет включать исторические записи из своего буфера записи производительности при первом вызове обратного вызова PerformanceObserver
, вплоть до максимального размера буфера для этого типа .
po.observe({
type: 'some-entry-type',
buffered: true,
});
Устаревшие API производительности, которых следует избегать
До API Performance Observer разработчики могли получать доступ к записям производительности, используя следующие три метода, определенные в объекте performance
:
Хотя эти API по-прежнему поддерживаются, их использование не рекомендуется, поскольку они не позволяют отслеживать появление новых записей. Кроме того, многие новые API (например, largest-contentful-paint
) не предоставляются через объект performance
, они предоставляются только через PerformanceObserver
.
Если вам не нужна совместимость с Internet Explorer, лучше избегать этих методов в своем коде и в дальнейшем использовать PerformanceObserver
.
API синхронизации пользователя
API пользовательского времени — это API общего назначения для измерения метрик, основанных на времени. Он позволяет произвольно отмечать точки во времени, а затем позже измерять продолжительность между этими отметками.
// Record the time immediately before running a task.
performance.mark('myTask:start');
await doMyTask();
// Record the time immediately after running a task.
performance.mark('myTask:end');
// Measure the delta between the start and end of the task
performance.measure('myTask', 'myTask:start', 'myTask:end');
Хотя такие API, как Date.now()
или performance.now()
предоставляют аналогичные возможности, преимущество использования User Timing API заключается в том, что он хорошо интегрируется с инструментами повышения производительности. Например, Chrome DevTools визуализирует измерения пользовательского времени на панели «Производительность» , а многие поставщики аналитики также автоматически отслеживают любые ваши измерения и отправляют данные о продолжительности на свой аналитический сервер.
Чтобы сообщить об измерениях пользовательского времени, вы можете использовать PerformanceObserver и зарегистрироваться для наблюдения за записями типа measure
:
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `measure` entries to be dispatched.
po.observe({type: 'measure', buffered: true});
API длинных задач
API длинных задач полезен для определения того, когда основной поток браузера блокируется на достаточно долгое время, чтобы повлиять на частоту кадров или задержку ввода. API будет сообщать обо всех задачах, которые выполняются дольше 50 миллисекунд.
Каждый раз, когда вам нужно запустить дорогостоящий код или загрузить и выполнить большие сценарии, полезно отслеживать, не блокирует ли этот код основной поток. Фактически, многие метрики более высокого уровня создаются поверх самого API длинных задач (например, время взаимодействия (TTI) и общее время блокировки (TBT) ).
Чтобы определить, когда выполняются длительные задачи, вы можете использовать PerformanceObserver и зарегистрироваться для наблюдения за записями типа longtask
:
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `longtask` entries to be dispatched.
po.observe({type: 'longtask', buffered: true});
API длинных кадров анимации
API длинных кадров анимации — это новая версия API длинных задач, которая рассматривает длинные кадры , а не длинные задачи , длительностью более 50 миллисекунд. Это устраняет некоторые недостатки API длинных задач , в том числе лучшую атрибуцию и более широкий спектр потенциально проблемных задержек.
Чтобы определить, когда происходят длинные кадры, вы можете использовать PerformanceObserver и зарегистрироваться для наблюдения за записями типа long-animation-frame
:
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `long-animation-frame` entries to be dispatched.
po.observe({type: 'long-animation-frame', buffered: true});
API синхронизации элементов
Метрика Largest Contentful Paint (LCP) полезна для определения того, когда на экране было нарисовано самое большое изображение или текстовый блок, но в некоторых случаях необходимо измерить время рендеринга другого элемента.
В этих случаях используйте Element Timing API . API LCP фактически построен на основе API синхронизации элементов и добавляет автоматические отчеты о самом большом элементе с содержимым, но вы также можете создавать отчеты о других элементах, явно добавляя к ним атрибут elementtiming
и регистрируя PerformanceObserver для наблюдения за типом записи element
.
<img elementtiming="hero-image" />
<p elementtiming="important-paragraph">This is text I care about.</p>
<!-- ... -->
<script>
const po = new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `element` entries to be dispatched.
po.observe({type: 'element', buffered: true});
</script>
API синхронизации событий
Метрика «Взаимодействие с следующей отрисовкой» (INP) оценивает общую скорость реагирования страницы, наблюдая за всеми взаимодействиями с щелчками, касаниями и клавиатурой на протяжении всего существования страницы. INP страницы чаще всего представляет собой взаимодействие, выполнение которого заняло больше всего времени: с момента, когда пользователь инициировал взаимодействие, до момента, когда браузер рисует следующий кадр, показывающий визуальный результат ввода пользователя.
Метрика INP стала возможной благодаря API синхронизации событий . Этот API предоставляет ряд временных меток, которые возникают в течение жизненного цикла события, в том числе:
-
startTime
: время, когда браузер получает событие. -
processingStart
: время, когда браузер может начать обработку обработчиков события. -
processingEnd
: время, когда браузер завершает выполнение всего синхронного кода, инициированного обработчиками событий для этого события. -
duration
: время (округленное до 8 миллисекунд по соображениям безопасности) между получением браузером события и возможностью отрисовки следующего кадра после завершения выполнения всего синхронного кода, инициированного обработчиками событий.
В следующем примере показано, как использовать эти значения для создания пользовательских измерений:
const po = new PerformanceObserver((entryList) => {
// Get the last interaction observed:
const entries = Array.from(entryList.getEntries()).forEach((entry) => {
// Get various bits of interaction data:
const inputDelay = entry.processingStart - entry.startTime;
const processingTime = entry.processingEnd - entry.processingStart;
const presentationDelay = entry.startTime + entry.duration - entry.processingEnd;
const duration = entry.duration;
const eventType = entry.name;
const target = entry.target || "(not set)"
console.log("----- INTERACTION -----");
console.log(`Input delay (ms): ${inputDelay}`);
console.log(`Event handler processing time (ms): ${processingTime}`);
console.log(`Presentation delay (ms): ${presentationDelay}`);
console.log(`Total event duration (ms): ${duration}`);
console.log(`Event type: ${eventType}`);
console.log(target);
});
});
// A durationThreshold of 16ms is necessary to include more
// interactions, since the default is 104ms. The minimum
// durationThreshold is 16ms.
po.observe({type: 'event', buffered: true, durationThreshold: 16});
API синхронизации ресурсов
API синхронизации ресурсов дает разработчикам подробную информацию о том, как были загружены ресурсы для конкретной страницы. Несмотря на название API, предоставляемая им информация не ограничивается только данными о времени (хотя их достаточно ). Другие данные, к которым вы можете получить доступ, включают:
-
initiatorType
: как был получен ресурс: например, из тега<script>
или<link>
или из вызоваfetch()
. -
nextHopProtocol
: протокол, используемый для получения ресурса, напримерh2
илиquic
. -
encodedBodySize
/ decodedBodySize ]: размер ресурса в закодированном или декодированном виде (соответственно) -
transferSize
: размер ресурса, который был фактически передан по сети. Когда ресурсы заполняются кешем, это значение может быть намного меньше, чемencodedBodySize
, а в некоторых случаях оно может быть нулевым (если повторная проверка кеша не требуется).
Вы можете использовать свойство transferSize
записей времени ресурсов для измерения показателя частоты попадания в кэш или показателя общего размера кэшированного ресурса , что может быть полезно для понимания того, как ваша стратегия кэширования ресурсов влияет на производительность для повторных посетителей.
В следующем примере регистрируются все ресурсы, запрошенные страницей, и указывается, был ли каждый ресурс обработан кэшем.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// If transferSize is 0, the resource was fulfilled using the cache.
console.log(entry.name, entry.transferSize === 0);
}
});
// Start listening for `resource` entries to be dispatched.
po.observe({type: 'resource', buffered: true});
API синхронизации навигации
API синхронизации навигации аналогичен API синхронизации ресурсов, но он сообщает только о запросах навигации . Тип записи navigation
также аналогичен типу записи resource
, но он содержит некоторую дополнительную информацию , специфичную только для запросов навигации (например, при возникновении событий DOMContentLoaded
и load
).
Одна метрика, которую многие разработчики отслеживают, чтобы понять время ответа сервера ( время до первого байта (TTFB) ), доступна с помощью API синхронизации навигации — в частности, это метка времени responseStart
записи.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// If transferSize is 0, the resource was fulfilled using the cache.
console.log('Time to first byte', entry.responseStart);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});
Еще одна метрика, которая может волновать разработчиков, использующих Service Worker, — это время запуска Service Worker для запросов навигации. Это количество времени, которое требуется браузеру для запуска рабочего потока службы, прежде чем он сможет начать перехватывать события выборки.
Время запуска сервис-воркера для конкретного запроса навигации можно определить по разнице между entry.responseStart
и entry.workerStart
.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Service Worker startup time:',
entry.responseStart - entry.workerStart);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});
API синхронизации сервера
API синхронизации сервера позволяет передавать данные о времени конкретного запроса с вашего сервера в браузер через заголовки ответов. Например, вы можете указать, сколько времени потребовалось для поиска данных в базе данных для определенного запроса, что может быть полезно при отладке проблем с производительностью, вызванных медленной работой сервера.
Для разработчиков, которые используют сторонних поставщиков аналитики, API синхронизации сервера — единственный способ сопоставить данные о производительности сервера с другими бизнес-показателями, которые могут измеряться этими инструментами аналитики.
Чтобы указать данные синхронизации сервера в ваших ответах, вы можете использовать заголовок ответа Server-Timing
. Вот пример.
HTTP/1.1 200 OK
Server-Timing: miss, db;dur=53, app;dur=47.2
Затем со своих страниц вы можете прочитать эти данные как о resource
, так и о записях navigation
из API-интерфейсов синхронизации ресурсов и времени навигации.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Logs all server timing data for this response
console.log('Server Timing', entry.serverTiming);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});
Очень важно иметь ориентированные на пользователя показатели , которые можно измерить повсеместно на любом веб-сайте. Эти показатели позволяют:
- Поймите, как реальные пользователи воспринимают Интернет в целом.
- Сравните свой сайт с сайтом конкурента.
- Отслеживайте полезные и полезные данные в своих аналитических инструментах без необходимости писать собственный код.
Универсальные метрики предлагают хорошую основу, но во многих случаях вам нужно измерять больше , чем просто эти метрики, чтобы получить полную информацию о вашем конкретном сайте.
Специальные метрики позволяют измерять аспекты взаимодействия с вашим сайтом, которые могут относиться только к нему, например:
- Сколько времени требуется одностраничному приложению (SPA) для перехода с одной «страницы» на другую.
- Сколько времени требуется странице для отображения данных, полученных из базы данных для вошедших в систему пользователей.
- Сколько времени требуется приложению, отображаемому на стороне сервера (SSR), для гидратации .
- Коэффициент попадания в кеш ресурсов, загруженных вернувшимися посетителями.
- Задержка событий щелчка или клавиатуры в игре.
API для измерения пользовательских метрик
Исторически у веб-разработчиков не было большого количества низкоуровневых API для измерения производительности, и в результате им приходилось прибегать к хакам, чтобы оценить, насколько хорошо работает сайт.
Например, можно определить, заблокирован ли основной поток из-за длительного выполнения задач JavaScript, запустив цикл requestAnimationFrame
и вычислив разницу между каждым кадром. Если разница значительно превышает частоту кадров дисплея, вы можете сообщить об этом как о длительной задаче. Однако такие хаки не рекомендуются, поскольку они сами по себе влияют на производительность (например, разряжая батарею).
Первое правило эффективного измерения производительности — убедиться, что ваши методы измерения производительности сами по себе не вызывают проблем с производительностью. Поэтому для любых специальных показателей, которые вы измеряете на своем сайте, лучше всего использовать один из следующих API, если это возможно.
API наблюдателя за производительностью
API Performance Observer — это механизм, который собирает и отображает данные из всех других API производительности, обсуждаемых на этой странице. Понимание этого имеет решающее значение для получения качественных данных.
Вы можете использовать PerformanceObserver
для пассивной подписки на события, связанные с производительностью. Это позволяет обратным вызовам API запускаться в периоды простоя , что означает, что они обычно не влияют на производительность страницы.
Чтобы создать PerformanceObserver
, передайте ему обратный вызов, который будет запускаться при отправке новых записей о производительности. Затем вы сообщаете наблюдателю, какие типы записей следует прослушивать, используя метод observe()
:
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
po.observe({type: 'some-entry-type'});
В следующих разделах перечислены все различные типы записей, доступные для наблюдения, но в новых браузерах вы можете проверить, какие типы записей доступны, с помощью статического свойства PerformanceObserver.supportedEntryTypes
.
Обратите внимание на записи, которые уже произошли
По умолчанию объекты PerformanceObserver
могут наблюдать записи только по мере их появления. Это может вызвать проблемы, если вы хотите отложенно загружать код аналитики производительности, чтобы он не блокировал ресурсы с более высоким приоритетом.
Чтобы получить исторические записи (после того, как они произошли), установите для флага buffered
значение true
при вызове observe()
. Браузер будет включать исторические записи из своего буфера записей производительности при первом вызове обратного вызова PerformanceObserver
, вплоть до максимального размера буфера для этого типа .
po.observe({
type: 'some-entry-type',
buffered: true,
});
Устаревшие API производительности, которых следует избегать
До API Performance Observer разработчики могли получать доступ к записям производительности, используя следующие три метода, определенные в объекте performance
:
Хотя эти API по-прежнему поддерживаются, их использование не рекомендуется, поскольку они не позволяют отслеживать появление новых записей. Кроме того, многие новые API (например, largest-contentful-paint
) не предоставляются через объект performance
, они предоставляются только через PerformanceObserver
.
Если вам не нужна совместимость с Internet Explorer, лучше избегать этих методов в своем коде и в дальнейшем использовать PerformanceObserver
.
API синхронизации пользователя
API пользовательского времени — это API общего назначения для измерения метрик, основанных на времени. Он позволяет произвольно отмечать точки во времени, а затем позже измерять продолжительность между этими отметками.
// Record the time immediately before running a task.
performance.mark('myTask:start');
await doMyTask();
// Record the time immediately after running a task.
performance.mark('myTask:end');
// Measure the delta between the start and end of the task
performance.measure('myTask', 'myTask:start', 'myTask:end');
Хотя такие API, как Date.now()
или performance.now()
предоставляют аналогичные возможности, преимущество использования User Timing API заключается в том, что он хорошо интегрируется с инструментами повышения производительности. Например, Chrome DevTools визуализирует измерения пользовательского времени на панели «Производительность» , а многие поставщики аналитики также автоматически отслеживают любые ваши измерения и отправляют данные о продолжительности на свой аналитический сервер.
Чтобы составить отчет об измерениях пользовательского времени, вы можете использовать PerformanceObserver и зарегистрироваться для наблюдения за записями типа measure
:
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `measure` entries to be dispatched.
po.observe({type: 'measure', buffered: true});
API длинных задач
API длинных задач полезен для определения того, когда основной поток браузера заблокирован на достаточно долгое время, чтобы повлиять на частоту кадров или задержку ввода. API будет сообщать обо всех задачах, которые выполняются дольше 50 миллисекунд.
Каждый раз, когда вам нужно запустить дорогостоящий код или загрузить и выполнить большие сценарии, полезно отслеживать, не блокирует ли этот код основной поток. Фактически, многие метрики более высокого уровня создаются поверх самого API длинных задач (например, время взаимодействия (TTI) и общее время блокировки (TBT) ).
Чтобы определить, когда выполняются длительные задачи, вы можете использовать PerformanceObserver и зарегистрироваться для наблюдения за записями типа longtask
:
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `longtask` entries to be dispatched.
po.observe({type: 'longtask', buffered: true});
API длинных кадров анимации
API длинных кадров анимации — это новая версия API длинных задач, которая рассматривает длинные кадры , а не длинные задачи , длительностью более 50 миллисекунд. Это устраняет некоторые недостатки API длинных задач , в том числе лучшую атрибуцию и более широкий спектр потенциально проблемных задержек.
Чтобы определить, когда происходят длинные кадры, вы можете использовать PerformanceObserver и зарегистрироваться для наблюдения за записями типа long-animation-frame
:
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `long-animation-frame` entries to be dispatched.
po.observe({type: 'long-animation-frame', buffered: true});
API синхронизации элементов
Метрика Largest Contentful Paint (LCP) полезна для определения того, когда на экране было нарисовано самое большое изображение или текстовый блок, но в некоторых случаях необходимо измерить время рендеринга другого элемента.
В этих случаях используйте Element Timing API . API LCP фактически построен на основе API синхронизации элементов и добавляет автоматические отчеты о самом большом элементе с содержимым, но вы также можете создавать отчеты о других элементах, явно добавляя к ним атрибут elementtiming
и регистрируя PerformanceObserver для наблюдения за типом записи element
.
<img elementtiming="hero-image" />
<p elementtiming="important-paragraph">This is text I care about.</p>
<!-- ... -->
<script>
const po = new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `element` entries to be dispatched.
po.observe({type: 'element', buffered: true});
</script>
API синхронизации событий
Метрика взаимодействия с следующей краской (INP) оценивает общую отзывчивость страницы, наблюдая за взаимодействием всех кликов, нажима и клавиатуры на протяжении всей жизни страницы. Чаще всего INP страницы - это взаимодействие, которое заняло самое длинное, от времени, когда пользователь инициировал взаимодействие, до того времени, когда браузер рисует следующий кадр, показывающий визуальный результат ввода пользователя.
Метрика INP стала возможной благодаря API времени события . Этот API раскрывает ряд метков времени, которые происходят в течение жизненного цикла событий, включая:
-
startTime
: время, когда браузер получает мероприятие. -
processingStart
: время, когда браузер может начать обработку обработчиков событий для события. -
processingEnd
: время, когда браузер заканчивает выполнение выполнения всего синхронного кода, инициированного от обработчиков событий для этого события. -
duration
: время (округлено до 8 миллисекунд по соображениям безопасности) между тем, когда браузер получает событие, пока не сможет нарисовать следующий кадр после завершения выполнения всего синхронного кода, инициированного от обработчиков событий.
В следующем примере показано, как использовать эти эти значения для создания пользовательских измерений:
const po = new PerformanceObserver((entryList) => {
// Get the last interaction observed:
const entries = Array.from(entryList.getEntries()).forEach((entry) => {
// Get various bits of interaction data:
const inputDelay = entry.processingStart - entry.startTime;
const processingTime = entry.processingEnd - entry.processingStart;
const presentationDelay = entry.startTime + entry.duration - entry.processingEnd;
const duration = entry.duration;
const eventType = entry.name;
const target = entry.target || "(not set)"
console.log("----- INTERACTION -----");
console.log(`Input delay (ms): ${inputDelay}`);
console.log(`Event handler processing time (ms): ${processingTime}`);
console.log(`Presentation delay (ms): ${presentationDelay}`);
console.log(`Total event duration (ms): ${duration}`);
console.log(`Event type: ${eventType}`);
console.log(target);
});
});
// A durationThreshold of 16ms is necessary to include more
// interactions, since the default is 104ms. The minimum
// durationThreshold is 16ms.
po.observe({type: 'event', buffered: true, durationThreshold: 16});
Resource Timing API
API Resource Diming дает разработчикам подробное представление о том, как были загружены ресурсы для конкретной страницы. Несмотря на название API, информация, которую он предоставляет, не ограничивается только данными о времени (хотя это много ). Другие данные, которые вы можете получить, включают:
-
initiatorType
: как был извлечен ресурс: например, из A<script>
или<link>
Tag или из вызоваfetch()
. -
nextHopProtocol
: протокол, используемый для извлечения ресурса, такого какh2
илиquic
. -
encodedBodySize
/ декодисбодизиз ]: размер ресурса в кодированной или декодированной форме (соответственно) -
transferSize
: размер ресурса, который фактически был передан по сети. Когда ресурсы выполняются в кеше, это значение может быть намного меньше, чемencodedBodySize
, и в некоторых случаях оно может быть нулевым (если не требуется повторная переоценка кэша).
Вы можете использовать свойство transferSize
of Resource Triming Recients для измерения показателя частоты попадания в кэш или общего показателя размера кэшированного ресурса , что может быть полезно для понимания того, как ваша стратегия кэширования ресурсов влияет на производительность для повторных посетителей.
В следующем примере регистрируется все ресурсы, запрошенные страницей, и указывает, был ли каждый ресурс выполнен кэшем.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// If transferSize is 0, the resource was fulfilled using the cache.
console.log(entry.name, entry.transferSize === 0);
}
});
// Start listening for `resource` entries to be dispatched.
po.observe({type: 'resource', buffered: true});
Навигационное время API
API навигационного времени аналогична API Resource Timing API, но он сообщает только о запросах на навигацию . Тип записи navigation
также похож на тип входа resource
, но он содержит некоторую дополнительную информацию , относящуюся только к запросам на навигации (например, когда DOMContentLoaded
и load
событий Fire).
Одна метрика, которую многие разработчики отслеживают, чтобы понять время отклика сервера ( время до первого байта (TTFB) ), доступна с использованием API навигационного времени - в частности, это время responseStart
.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// If transferSize is 0, the resource was fulfilled using the cache.
console.log('Time to first byte', entry.responseStart);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});
Другими разработчиками метрики, которые используют работника обслуживания, могут заботиться о времени стартапа работника службы для запросов навигации. Это количество времени, которое требуется в браузере, чтобы запустить поток обслуживания работника, прежде чем он сможет начать перехватывать события.
Время запуска работника службы для конкретного запроса навигации может быть определена из дельты между entry.responseStart
и entry.workerStart
.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Service Worker startup time:',
entry.responseStart - entry.workerStart);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});
API сервера
API Server Timing API позволил вам передавать данные о времени, специфичные для запроса с вашего сервера в браузер через заголовки ответов. Например, вы можете указать, сколько времени понадобится для поиска данных в базе данных для конкретного запроса, что может быть полезно при отладке проблем с производительностью, вызванной медленей на сервере.
Для разработчиков, которые используют сторонние поставщики аналитики, API сервера-единственный способ корреляции данных о производительности сервера с другими бизнес-метриками, которые могут измерять эти инструменты аналитики.
Чтобы указать данные о времени сервера в ваших ответах, вы можете использовать заголовок ответа Server-Timing
. Вот пример.
HTTP/1.1 200 OK
Server-Timing: miss, db;dur=53, app;dur=47.2
Затем, на ваших страницах, вы можете прочитать эти данные как в resource
, так и на navigation
записях из API -интерфейсов времени ресурсов и времени навигации.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Logs all server timing data for this response
console.log('Server Timing', entry.serverTiming);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});