Оценка производительности погрузки в полевых условиях с помощью Navigation Timing и Resource Timing.

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

Если вы использовали регулирование соединения на панели сети в инструментах разработчика браузера (или Lighthouse в Chrome) для оценки производительности загрузки, вы знаете, насколько удобны эти инструменты для настройки производительности. Вы можете быстро оценить влияние оптимизации производительности с помощью постоянной и стабильной базовой скорости соединения. Единственная проблема в том, что это синтетическое тестирование, которое дает лабораторные данные , а не полевые данные .

Синтетическое тестирование по своей сути не является плохим , но оно не отражает того, насколько быстро ваш сайт загружается для реальных пользователей. Для этого требуются данные полей, которые вы можете собрать из API-интерфейсов Navigation Timing и Resource Timing.

API, которые помогут вам оценить производительность загрузки в полевых условиях

Время навигации и время ресурса — это два похожих API со значительным перекрытием, которые измеряют две разные вещи:

  • Время навигации измеряет скорость запросов к HTML-документам (то есть запросов навигации).
  • Resource Timing измеряет скорость запросов к ресурсам, зависящим от документа, таким как CSS, JavaScript, изображения и т. д.

Эти API предоставляют свои данные в буфере ввода производительности , доступ к которому можно получить в браузере с помощью JavaScript. Существует несколько способов запроса буфера производительности, но наиболее распространенным способом является использование performance.getEntriesByType :

// Get Navigation Timing entries:
performance.getEntriesByType('navigation');

// Get Resource Timing entries:
performance.getEntriesByType('resource');

performance.getEntriesByType принимает строку, описывающую тип записей, которые вы хотите получить из буфера записей производительности. 'navigation' и 'resource' извлекают время для API-интерфейсов Navigation Timing и Resource Timing соответственно.

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

Время жизни и время выполнения сетевого запроса

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

Диаграмма таймингов сети, показанная в DevTools Chrome. Изображены сроки постановки запросов в очередь, согласования соединения, самого запроса и ответа в цветных полосах.
Визуализация сетевого запроса на сетевой панели Chrome DevTools.

Жизнь сетевого запроса состоит из отдельных этапов, таких как поиск DNS, установление соединения, согласование TLS и т. д. Эти тайминги представлены как DOMHighResTimestamp . В зависимости от вашего браузера точность таймингов может достигать микросекунд или округляться до миллисекунд. Давайте подробно рассмотрим эти этапы и то, как они связаны со временем навигации и временем ресурсов.

DNS-поиск

Когда пользователь переходит по URL-адресу, система доменных имен (DNS) запрашивает преобразование домена в IP-адрес. Этот процесс может занять значительное время — даже время, которое вы захотите измерить в полевых условиях. Время навигации и время ресурса предоставляют два времени, связанных с DNS:

  • domainLookupStart — это начало поиска DNS.
  • domainLookupEnd — это завершение поиска DNS.

Вычислить общее время поиска DNS можно путем вычитания начальной метрики из конечной метрики:

// Measuring DNS lookup time
const [pageNav] = performance.getEntriesByType('navigation');
const totalLookupTime = pageNav.domainLookupEnd - pageNav.domainLookupStart;

Согласование соединения

Еще одним фактором, влияющим на производительность загрузки, является согласование соединения, которое связано с задержкой при подключении к веб-серверу. Если задействован HTTPS, этот процесс также будет включать время согласования TLS. Фаза подключения состоит из трех таймингов:

  • connectStart — это когда браузер начинает открывать соединение с веб-сервером.
  • secureConnectionStart отмечает, когда клиент начинает согласование TLS.
  • connectEnd — это когда соединение с веб-сервером установлено.

Измерение общего времени соединения аналогично измерению общего времени поиска DNS: вы вычитаете время начала из времени окончания. Однако существует дополнительное свойство secureConnectionStart , которое может иметь значение 0 , если HTTPS не используется или соединение постоянное . Если вы хотите измерить время согласования TLS, вам необходимо иметь это в виду:

// Quantifying total connection time
const [pageNav] = performance.getEntriesByType('navigation');
const connectionTime = pageNav.connectEnd - pageNav.connectStart;
let tlsTime = 0; // <-- Assume 0 to start with

// Was there TLS negotiation?
if (pageNav.secureConnectionStart > 0) {
  // Awesome! Calculate it!
  tlsTime = pageNav.connectEnd - pageNav.secureConnectionStart;
}

После завершения поиска DNS и согласования соединения в игру вступают сроки, связанные с получением документов и зависимых от них ресурсов.

Запросы и ответы

На производительность нагрузки влияют два типа факторов:

  • Внешние факторы: это такие вещи, как задержка и пропускная способность. Помимо выбора хостинговой компании и CDN, они (в основном) находятся вне нашего контроля, поскольку пользователи могут получить доступ к Интернету из любого места.
  • Внутренние факторы: это такие вещи, как серверная и клиентская архитектура, а также размер ресурсов и наша способность оптимизировать те вещи, которые находятся под нашим контролем.

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

  • fetchStart отмечает, когда браузер начинает извлекать ресурс (время ресурса) или документ для запроса навигации (время навигации). Это предшествует фактическому запросу и является моментом, когда браузер проверяет кеши (например, экземпляры HTTP и Cache ).
  • workerStart отмечает, когда запрос начинает обрабатываться в обработчике событий fetch сервис-воркера. Это значение будет равно 0 , если ни один сервисный работник не контролирует текущую страницу.
  • requestStart — это когда браузер отправляет запрос.
  • responseStart — это момент поступления первого байта ответа.
  • responseEnd — это когда приходит последний байт ответа.

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

// Cache seek plus response time of the current document
const [pageNav] = performance.getEntriesByType('navigation');
const fetchTime = pageNav.responseEnd - pageNav.fetchStart;

// Service worker time plus response time
let workerTime = 0;

if (pageNav.workerStart > 0) {
  workerTime = pageNav.responseEnd - pageNav.workerStart;
}

Вы также можете измерить другие аспекты задержки запроса/ответа:

const [pageNav] = performance.getEntriesByType('navigation');

// Request time only (excluding redirects, DNS, and connection/TLS time)
const requestTime = pageNav.responseStart - pageNav.requestStart;

// Response time only (download)
const responseTime = pageNav.responseEnd - pageNav.responseStart;

// Request + response time
const requestResponseTime = pageNav.responseEnd - pageNav.requestStart;

Другие измерения, которые вы можете сделать

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

  • Перенаправления страниц: перенаправления являются упущенным из виду источником дополнительной задержки, особенно цепочки перенаправлений. Задержка добавляется несколькими способами, такими как переходы HTTP-HTTP, а также перенаправления 302/некэшированные 301. Тайминги redirectStart , redirectEnd и redirectCount полезны при оценке задержки перенаправления.
  • Выгрузка документа: на страницах, на которых в обработчике события unload выполняется код, браузер должен выполнить этот код, прежде чем он сможет перейти к следующей странице. unloadEventStart и unloadEventEnd измеряют выгрузку документа.
  • Обработка документов. Время обработки документов может не иметь существенного значения, если только ваш веб-сайт не отправляет очень большие полезные данные HTML. Если это описывает вашу ситуацию, тайминги domInteractive , domContentLoadedEventStart , domContentLoadedEventEnd и domComplete могут представлять интерес.

Получение таймингов в коде приложения

Во всех примерах, показанных до сих пор, используется performance.getEntriesByType , но есть и другие способы запроса буфера записи производительности, например performance.getEntriesByName и performance.getEntries . Эти методы хороши, когда необходим только анализ света. Однако в других ситуациях они могут привести к чрезмерной работе основного потока, перебирая большое количество записей или даже многократно опрашивая буфер производительности для поиска новых записей.

Рекомендуемый подход для сбора записей из буфера записей производительности — использовать PerformanceObserver . PerformanceObserver прослушивает записи производительности и предоставляет их по мере добавления в буфер:

// Create the performance observer:
const perfObserver = new PerformanceObserver((observedEntries) => {
  // Get all resource entries collected so far:
  const entries = observedEntries.getEntries();

  // Iterate over entries:
  for (let i = 0; i < entries.length; i++) {
    // Do the work!
  }
});

// Run the observer for Navigation Timing entries:
perfObserver.observe({
  type: 'navigation',
  buffered: true
});

// Run the observer for Resource Timing entries:
perfObserver.observe({
  type: 'resource',
  buffered: true
});

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

Звонок домой

Собрав все необходимые данные о времени, вы можете отправить их в конечную точку для дальнейшего анализа. Это можно сделать двумя способами: либо с помощью navigator.sendBeacon , либо с fetch с установленной опцией keepalive . Оба метода отправят запрос в указанную конечную точку неблокирующим способом, и запрос будет поставлен в очередь таким образом, чтобы при необходимости пережить текущий сеанс страницы:

// Caution: If you have lots of performance entries, don't
// do this. This is an example for illustrative purposes.
const data = JSON.stringify(performance.getEntries()));

// The endpoint to transmit the encoded data to
const endpoint = '/analytics';

// Check for fetch keepalive support
if ('keepalive' in Request.prototype) {
  fetch(endpoint, {
    method: 'POST',
    body: data,
    keepalive: true,
    headers: {
      'Content-Type': 'application/json'
    }
  });
} else if ('sendBeacon' in navigator) {
  // Use sendBeacon as a fallback
  navigator.sendBeacon(endpoint, data);
}

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

Подведение итогов

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

  • Избегайте средних значений , поскольку они не отражают впечатления какого-либо пользователя и могут быть искажены выбросами.
  • Полагайтесь на процентили. В наборах данных показателей производительности, основанных на времени, чем меньше, тем лучше. Это означает, что, отдавая приоритет низким процентилям, вы обращаете внимание только на самые быстрые результаты.
  • Отдавайте приоритет длинному хвосту ценностей . Когда вы отдаете приоритет событиям на уровне 75-го процентиля или выше, вы сосредотачиваете свое внимание там, где оно должно быть: на самых медленных впечатлениях.

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

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