Как начать работу с Performance API в JavaScript
Производительность, производительность, производительность. У вас может быть лучший сайт в мире, но если загрузка займет 2 минуты, его никто не увидит. Если ваш сайт загружается за 2 минуты, вероятно, не составит труда понять, почему. Оптимизация сложнее, когда вы пытаетесь снизить среднее время загрузки с 1 до 0,85 секунды.
Существует множество инструментов, которые могут помочь вам понять, как ваше приложение работает локально. Performance API здесь, чтобы помочь нам получить детальное представление о наших веб-страницах. Вы можете получить реальные данные и посмотреть, как ваш сайт работает в разных браузерах, сетях, частях света и многом другом!
Performance API часто описывается как совокупность API. Слишком много вещей, чтобы описать все это в одной статье. В этом посте мы покажем самые основные функции, которые помогут вам начать мониторинг производительности.
API развивается, и есть много новых функций и устаревших функций. 2-й уровень всех API-интерфейсов для повышения производительности; некоторые из них частично реализованы, некоторые все еще являются черновиками. Поэтому вам следует регулярно проверять MDN или веб-сайт W3C на наличие последних обновлений.
Как получить доступ к данным о производительности
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: записи 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
//}
Аудит ваших функций
Для аудита определенных функций 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')
Вот один из ресурсов, найденных на моем сайте:
{
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. Поэтому следующие свойства всегда будут возвращать 0: redirectStart, redirectEnd, domainLookupStart, domainLookupEnd, connectStart, connectEnd, secureConnectionStart, requestStart и responseStart.
Отрисовка
API рисования относится к событиям, которые рисуют пиксели в окне. Как мы видели в предыдущем фрагменте, у нас есть доступ к First Time to Paint и First Contentful Paint. Если вы работали с инструментами внешней оптимизации, такими как Lighthouse, вы можете быть знакомы с этими терминами. First Time to Paint - это когда первый пиксель появляется на экране пользователя. First Contentful Paint - это когда элемент, определенный в DOM, начал отображается. Для оптимизации First Time to Paint вы можете уменьшить количество сценариев и таблиц стилей, блокирующих рендеринг, использовать HTTP-кэширование, оптимизировать загрузку JavaScript и многое другое!
Это полезные показатели, но они довольно ограничены, если вы пытаетесь понять, что видят ваши пользователи. Чтобы иметь представление о восприятии производительности вашего пользователя, нам нужно объединить несколько показателей.
API производительности гигантский и быстро меняется. Если вы хотите глубоко изучить эту тему, вам следует посетить страницу рабочей группы по веб-производительности, где можно найти последние рабочие проекты и рекомендации.