Пользовательские метрики

Очень важно иметь ориентированные на пользователя показатели , которые можно измерить повсеместно на любом веб-сайте. Эти показатели позволяют:

  • Поймите, как реальные пользователи воспринимают Интернет в целом.
  • Сравните свой сайт с сайтом конкурента.
  • Отслеживайте полезные и полезные данные в своих аналитических инструментах без необходимости писать собственный код.

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

Специальные метрики позволяют измерять аспекты взаимодействия с вашим сайтом, которые могут относиться только к нему, например:

  • Сколько времени требуется одностраничному приложению (SPA) для перехода с одной «страницы» на другую.
  • Сколько времени требуется странице для отображения данных, полученных из базы данных для вошедших в систему пользователей.
  • Сколько времени требуется приложению, отображаемому на стороне сервера (SSR), для гидратации .
  • Коэффициент попадания в кеш ресурсов, загруженных вернувшимися посетителями.
  • Задержка событий щелчка или клавиатуры в игре.

API для измерения пользовательских метрик

Исторически у веб-разработчиков не было большого количества низкоуровневых API для измерения производительности, и в результате им приходилось прибегать к хакам, чтобы оценить, хорошо ли работает сайт.

Например, можно определить, заблокирован ли основной поток из-за длительного выполнения задач JavaScript, запустив цикл requestAnimationFrame и вычислив дельту между каждым кадром. Если разница значительно превышает частоту кадров дисплея, вы можете сообщить об этом как о длительной задаче. Однако такие хаки не рекомендуются, поскольку они сами по себе влияют на производительность (например, разряжая батарею).

Первое правило эффективного измерения производительности — убедиться, что ваши методы измерения производительности сами по себе не вызывают проблем с производительностью. Поэтому для любых специальных показателей, которые вы измеряете на своем сайте, лучше всего использовать один из следующих API, если это возможно.

API наблюдателя за производительностью

Поддержка браузера

  • 52
  • 79
  • 57
  • 11

Источник

API Performance Observer — это механизм, который собирает и отображает данные из всех других API производительности, обсуждаемых на этой странице. Понимание этого имеет решающее значение для получения качественных данных.

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

Чтобы создать PerformanceObserver , передайте ему обратный вызов, который будет запускаться при отправке новых записей о производительности. Затем вы сообщаете наблюдателю, какие типы записей следует прослушивать, используя метод observe() :

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  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'});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

В следующих разделах перечислены все различные типы записей, доступные для наблюдения, но в новых браузерах вы можете проверить, какие типы записей доступны, с помощью статического свойства PerformanceObserver.supportedEntryTypes .

Обратите внимание на записи, которые уже произошли

По умолчанию объекты PerformanceObserver могут наблюдать записи только по мере их появления. Это может вызвать проблемы, если вы хотите отложенно загружать код аналитики производительности, чтобы он не блокировал ресурсы с более высоким приоритетом.

Чтобы получить исторические записи (после того, как они произошли), установите для флага buffered значение true при вызове observe() . Браузер будет включать исторические записи из своего буфера записей производительности при первом вызове обратного вызова PerformanceObserver .

po.observe({
  type: 'some-entry-type',
  buffered: true,
});

Устаревшие API производительности, которых следует избегать

До API Performance Observer разработчики могли получать доступ к записям производительности, используя следующие три метода, определенные в объекте performance :

Хотя эти API по-прежнему поддерживаются, их использование не рекомендуется, поскольку они не позволяют отслеживать появление новых записей. Кроме того, многие новые API (например, Long Tasks) не предоставляются через объект 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() предоставляют аналогичные возможности, преимущество использования API User Timing заключается в том, что он хорошо интегрируется с инструментами повышения производительности. Например, Chrome DevTools визуализирует измерения пользовательского времени на панели «Производительность» , а многие поставщики аналитики также автоматически отслеживают любые ваши измерения и отправляют данные о продолжительности на свой аналитический сервер.

Чтобы составить отчет об измерениях пользовательского времени, вы можете использовать PerformanceObserver и зарегистрироваться для наблюдения за записями типа measure :

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

API длинных задач

Поддержка браузера

  • 58
  • 79
  • Икс
  • Икс

Источник

API длинных задач полезен для определения того, когда основной поток браузера блокируется на достаточно долгое время, чтобы повлиять на частоту кадров или задержку ввода. API будет сообщать обо всех задачах, которые выполняются дольше 50 миллисекунд.

Каждый раз, когда вам нужно запустить дорогостоящий код или загрузить и выполнить большие сценарии, полезно отслеживать, не блокирует ли этот код основной поток. Фактически, многие метрики более высокого уровня создаются поверх самого API длинных задач (например , время взаимодействия (TTI) и общее время блокировки (TBT) ).

Чтобы определить, когда выполняются длительные задачи, вы можете использовать PerformanceObserver и зарегистрироваться для наблюдения за записями типа longtask :

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

API синхронизации элементов

Поддержка браузера

  • 77
  • 79
  • Икс
  • Икс

Источник

Метрика 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>
  // Catch errors since some browsers throw when using the new `type` option.
  // https://bugs.webkit.org/show_bug.cgi?id=209216
  try {
    // Create the performance observer.
    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});
  } catch (e) {
    // Do nothing if the browser doesn't support this API.
  }
</script>

API синхронизации событий

Метрика «Взаимодействие с следующей отрисовкой» (INP) оценивает общую скорость реагирования страницы, наблюдая за всеми взаимодействиями с щелчками, касаниями и клавиатурой на протяжении всего существования страницы. INP страницы чаще всего представляет собой взаимодействие, выполнение которого заняло больше всего времени: с момента, когда пользователь инициировал взаимодействие, до момента, когда браузер рисует следующий кадр, показывающий визуальный результат ввода пользователя.

Метрика INP стала возможной благодаря API синхронизации событий . Этот API предоставляет ряд временных меток, которые возникают в течение жизненного цикла события, в том числе:

  • startTime : время, когда браузер получает событие.
  • processingStart : время, когда браузер может начать обработку обработчиков события.
  • processingEnd : время, когда браузер завершает выполнение всего синхронного кода, инициированного обработчиками событий для этого события.
  • duration : время (округленное до 8 миллисекунд по соображениям безопасности) между получением браузером события и возможностью отрисовки следующего кадра после завершения выполнения всего синхронного кода, инициированного обработчиками событий.

В следующем примере показано, как использовать эти значения для создания пользовательских измерений:

// Catch errors some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  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 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 time (ms): ${processingTime}`);
      console.log(`Total event duration (ms): ${duration}`);
      console.log(`Event type: ${eventType}`);
      console.log(target);
    });
  });

  // A durationThreshold of 16ms is necessary to surface more
  // interactions, since the default is 104ms. The minimum
  // durationThreshold is 16ms.
  po.observe({type: 'event', buffered: true, durationThreshold: 16});
} catch (error) {
  // Do nothing if the browser doesn't support this API.
}

API синхронизации ресурсов

API синхронизации ресурсов дает разработчикам подробную информацию о том, как были загружены ресурсы для конкретной страницы. Несмотря на название API, предоставляемая им информация не ограничивается только данными о времени (хотя их достаточно ). Другие данные, к которым вы можете получить доступ, включают:

  • initiatorType : как был получен ресурс: например, из тега <script> или <link> или из вызова fetch() .
  • nextHopProtocol : протокол, используемый для получения ресурса, например h2 или quic .
  • encodedBodySize / decodedBodySize ]: размер ресурса в закодированном или декодированном виде (соответственно)
  • transferSize : размер ресурса, который был фактически передан по сети. Когда ресурсы выполняются кэшем, это значение может быть намного меньше, чем encodedBodySize , а в некоторых случаях оно может быть нулевым (если повторная проверка кэша не требуется).

Вы можете использовать свойство transferSize записей времени ресурсов для измерения показателя частоты попадания в кэш или показателя общего размера кэшированного ресурса , что может быть полезно для понимания того, как ваша стратегия кэширования ресурсов влияет на производительность для повторных посетителей.

В следующем примере регистрируются все ресурсы, запрошенные страницей, и указывается, был ли каждый ресурс обработан кэшем.

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // If transferSize is 0, the resource was fulfilled via the cache.
      console.log(entry.name, entry.transferSize === 0);
    }
  });

  // Start listening for `resource` entries to be dispatched.
  po.observe({type: 'resource', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

Поддержка браузера

  • 57
  • 12
  • 58
  • 15

Источник

API синхронизации навигации аналогичен API синхронизации ресурсов, но он сообщает только о запросах навигации . Тип записи navigation также аналогичен типу записи resource , но он содержит некоторую дополнительную информацию, специфичную только для запросов навигации (например, при возникновении событий DOMContentLoaded и load ).

Одна метрика, которую многие разработчики отслеживают, чтобы понять время ответа сервера ( время до первого байта (TTFB) ), доступна с помощью API синхронизации навигации — в частности, это метка времени responseStart записи.

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // If transferSize is 0, the resource was fulfilled via the cache.
      console.log('Time to first byte', entry.responseStart);
    }
  });

  // Start listening for `navigation` entries to be dispatched.
  po.observe({type: 'navigation', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

Еще одна метрика, которая может волновать разработчиков, использующих Service Worker, — это время запуска Service Worker для запросов навигации. Это количество времени, которое требуется браузеру для запуска рабочего потока службы, прежде чем он сможет начать перехватывать события выборки.

Время запуска сервис-воркера для конкретного запроса навигации можно определить по разнице между entry.responseStart и entry.workerStart .

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

API синхронизации сервера

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

Для разработчиков, которые используют сторонних поставщиков аналитики, API синхронизации сервера — единственный способ сопоставить данные о производительности сервера с другими бизнес-показателями, которые могут измеряться этими инструментами аналитики.

Чтобы указать данные синхронизации сервера в ваших ответах, вы можете использовать заголовок ответа Server-Timing . Вот пример.

HTTP/1.1 200 OK

Server-Timing: miss, db;dur=53, app;dur=47.2

Затем со своих страниц вы можете прочитать эти данные как о resource , так и о записях navigation из API-интерфейсов синхронизации ресурсов и времени навигации.

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}