Начало работы с JavaScript Performance API

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

Однако если ваш сайт загружается 2 минуты, найти и устранить причину не составит труда. Гораздо сложнее оптимизировать сайт, среднее время загрузки которого – 1 секунда, а вы хотите сократить его до 0,85 секунды.

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

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

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

Читайте также: Получение данных с помощью JavaScript Fetch API

Как получить доступ к данным о производительности

Метод performance.now

Самый простой способ измерить производительность программы — использовать performance.now(). Этот метод вернет текущее время в миллисекундах. Если вы хотите изучить время в высоком разрешении, настоятельно рекомендуем прочитать материал редактора W3C по этой теме.

performance.now позволяет вам измерять только то, что находится в вашем коде JavaScript (то есть производительность пользователя). Позже в этом посте мы рассмотрим пример использования performance.now.

Для доступа к различным событиям DOM и браузера у нас есть 3 функции:

getEntries() возвращает все доступные записи о производительности. Попробуйте запустить performance.getEntries() на текущей странице, и вы увидите большой массив. Изначально большинство записей будут относиться ко всем изображениям, сценариям и другим вещам, которые загружаются страницей (то есть к ресурсам).

const tenthEntry = performance.getEntries()[10]
// on Alligator.io it will return the following object
// { initiatorType: "script",
// nextHopProtocol: "h2",
// workerStart: 526.8099999520928,
// redirectStart: 0,
// ....
// decodedBodySize: 0,
// serverTiming: [],
// name: "https://d33wubrfki0l68.cloudfront.net/bundles/e2203d1b1c14952473222bcff4c58a8bd9fef14a.js",
// entryType: "resource",
// startTime: 315.5049999477342,
// duration: 231.48499999661
//}
// We can see this is a resource entry for a script loaded from cloudfront

Функция getEntriesByType() похожа на getEntries(), но она дает вам возможность фильтровать результаты.

Она позволяет запрашивать 6 типов:

  • frame: экспериментальная функция, позволяющая разработчикам получать данные о том, сколько работы проделал браузер за один цикл обработки событий. Если браузер выполняет слишком много работы в одном цикле, частота падает, а пользовательский опыт будет плохим.
  • resource: относится ко всем ресурсам, которые загружаются сайтом.
  • mark: пользовательские маркеры, которые можно использовать для расчета скорости вашего кода.
  • measure: позволяет легко измерить разницу между двумя отметками.
  • paint: пиксели, отображаемые на экране.
  • longtask: длинные задачи — это любые задачи, выполнение которых занимает более 50 мс.

Мы рассмотрим некоторые из этих типов в следующих разделах руководства. Для начала давайте посмотрим на простой пример:

const paintEntries = performance.getEntriesByType('paint')
// paint Entries[0] equals {
//    name: "first-paint",
//    entryType: "paint",
//    startTime: 342.160000000149,
//    duration: 0,
//    }
// paintEntries[1] equals {
//    name: "first-contentful-paint",
//    entryType: "paint",
//    startTime: 342.160000000149,
//    duration: 0,
// }

getEntriesByName(entryName) фильтрует все записи по имени.
const nativeLogoPerfEntry = performance.getEntriesByName('https://alligator.io/images/alligator-logo3.svg')[0];
// It will return performance information related to the logo's performance:
// {initiatorType: "img",
// nextHopProtocol: "",
// workerStart: 539.6649999311194,
// ........
// name: "https://alligator.io/images/alligator-logo3.svg",
// entryType: "resource",
// startTime: 539.5149999530986,
// duration: 94.24000000581145
//}

Примечание: Если вам нужна информация более высокого уровня о производительности сайта, вы также можете вызвать performance.toJSON().

Аудит функций

Самым основным инструментом аудита определенных функций JavaScript является performance.now(), о котором мы говорили выше.

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

const firstNow = performance.now()
// This loop is just to simulate slow calculations
for (let i = 0; i < 100000; i++){
  var ii = Math.sqrt(i)
}
const secondNow = performance.now()

const howLongDidOurLoopTake = secondNow - firstNow
// on my laptop it returns howLongDidOurLoopTake == 4.08500000089407 in milliseconds

Проблема с now в том, что с ним немного сложно работать, если у вас много показателей. Более полезным инструментом является функция mark, которая создает записи о производительности, которые вы можете запросить позже. Кроме того, вы можете комбинировать маркеры и создавать новые записи, используя measure.

performance.mark('beginSquareRootLoop');
// This loop is just to simulate slow calculations
for (let i = 0; i < 1000000; i++){
  var ii = Math.sqrt(i);
}
performance.mark('endSquareRootLoop');
// Then anywhere in your code you can use

// We create a new entry called measureSquareRootLoop which combines our two marks
performance.measure('measureSquareRootLoop','beginSquareRootLoop', 'endSquareRootLoop');

console.log(performance.getEntriesByName('beginSquareRootLoop'));
// {detail: null,
// name: "beginSquareRootLoop",
// entryType: "mark",
// startTime: 3745.360000000801,
// duration: 0}

console.log(performance.getEntriesByName('measureSquareRootLoop'));
// {detail: null,
// name: "measureSquareRootLoop",
// entryType: "measure",
// startTime: 3745.360000000801, This is the same as beginSquareRootLoop
// duration: 9.904999984428287 shows the time it took to get from beginSquareRootLoop to endSquareRootLoop
//}

Навигационные данные

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

const navigationEntry = performance.getEntriesByType('navigation')[0]

Мы получили такой результат:

{
  unloadEventStart: 213.41000002576038,
  unloadEventEnd: 213.41000002576038,
  domInteractive: 975.8100000326522,
  domContentLoadedEventStart: 982.2649999987334,
  domContentLoadedEventEnd: 1217.9650000180118,
  domComplete: 2000.960000033956,
  loadEventStart: 2001.044999982696,
  loadEventEnd: 2008.6500000325032,
  type: "reload",
  redirectCount: 0,
  initiatorType: "navigation",
  nextHopProtocol: "",
  workerStart: 2.5550000136718154,
  redirectStart: 0,
  redirectEnd: 0,
  fetchStart: 2.5599999935366213,
  domainLookupStart: 2.5599999935366213,
  domainLookupEnd: 2.5599999935366213,
  connectStart: 2.5599999935366213,
  connectEnd: 2.5599999935366213,
  secureConnectionStart: 0,
  requestStart: 2.5599999935366213,
  responseStart: 107.46500000823289,
  responseEnd: 214.3950000172481,
  transferSize: 0,
  encodedBodySize: 0,
  decodedBodySize: 0,
  serverTiming: [],
  name: "https://alligator.io/",
  entryType: "navigation",
  startTime: 0,
  duration: 2008.6500000325032
}

Ресурсы

Каждый раз, когда страница загружает ресурс, мы можем найти его след в записях о производительности. Все, что нам нужно сделать, чтобы получить их, это запустить performance.getEntriesByType(‘resource’). К этим ресурсам относятся изображения, сценарии, файлы CSS и многое другое. Так, например, если мы хотим сосредоточиться на производительности изображений на сайте, мы можем запустить:

performance.getEntriesByType('resource').filter(resource=> resource.initiatorType == 'img')

Вот один из ресурсов, найденных на Alligator.io:

{
    initiatorType: "img",
    nextHopProtocol: "h2",
    workerStart: 551.2149999849498,
    redirectStart: 0,
    redirectEnd: 0,
    fetchStart: 551.3149999896996,
    domainLookupStart: 0,
    domainLookupEnd: 0,
    connectStart: 0,
    connectEnd: 0,
    secureConnectionStart: 0,
    requestStart: 0,
    responseStart: 0,
    responseEnd: 560.1850000093691,
    transferSize: 0,
    encodedBodySize: 0,
    decodedBodySize: 0,
    serverTiming: [],
    name: "https://d33wubrfki0l68.cloudfront.net/39d2d2905588dad289b228deb021d51449f6143d/a3baf/images/logos/gatsby-logo.svg",
    entryType: "resource",
    startTime: 222.0450000022538,
    duration: 338.1400000071153
}

Как вы можете видеть, эта запись имеет много значений 0 из-за ограничений CORS (это большой предел API синхронизации ресурсов). Таким образом, следующие свойства всегда будут возвращать 0: redirectStart, redirectEnd, domainLookupStart, domainLookupEnd, connectStart, connectEnd, secureConnectionStart, requestStart и responseStart.

paint API

Paint API относится к событиям, которые рисуют пиксели в окне. Как мы видели в предыдущем фрагменте, у нас есть доступ к First Time to Paint и First Contentful Paint. Если вы работали с инструментами оптимизации внешнего интерфейса, такими как Lighthouse, вы должны быть знакомы с этими терминами. First Time to Paint – это когда на экране пользователя появляется первый пиксель. First Contentful Paint, первая содержательная отрисовка — это когда элемент, определенный в DOM, впервые визуализируется. Чтобы оптимизировать первую содержательную отрисовку, вы можете уменьшить количество скриптов и таблиц стилей, блокирующих рендеринг, использовать кэширование HTTP, оптимизировать загрузку JavaScript и многое другое!

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

Заключение

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

Tags: ,

Добавить комментарий