Примечание редактора. Автором этой статьи является Лю Юйчэнь, фронтенд-разработчик из 360 Qi Dance Company и член рабочей группы W3C Performance.
В прошлый раз автор представилNavigation Timing
а такжеResource Timing
Практическое приложение для мониторинга загрузки страниц.
На этот раз автор познакомит вас сPerformance Timeline
а такжеUser Timing
Standard, и используйте соответствующий API для «оценки» внешнего кода.
Зачем изучать эти два стандарта?
В реальном бизнесе иногда возникают операции, требующие высокой производительности, особенно частое использование DOM. Так как же количественно оценить производительность этих операций?
Обычной практикой является запись функций до и после выполнения функции отдельно.Date.now()
, а затем возьмите разницу, чтобы получить конкретное время выполнения.
Хорошо записывать одну-две функции, если их больше, разработчику нужно вести глобальный хэш для подсчета всех данных.
вместе сPerformance Timeline
+ User Timing
С введением стандарта разработчики могут напрямую использовать соответствующий API, а браузер будет напрямую считать релевантную информацию, что значительно упрощает процесс измерения производительности интерфейса.
чтоPerformance Timeline
?
Согласно определению W3C,Performance Timeline
Он предназначен для помощи веб-разработчикам в доступе, обнаружении и получении различных показателей производительности на протяжении всего жизненного цикла веб-приложений, а также в определении связанных интерфейсов.
чтоUser Timing
?
User Timing
В сравнении сPerformance Timeline
Подробнее. Этот стандарт расширяет первоначальныйPerformance
интерфейс, а также добавлены новые методы для фронтенд-разработчиков для активной записи показателей производительности.
По состоянию на 29 июля 2018 г.Performance Timeline
а такжеUser Timing
Последние стандартыLevel 2
, и оба находятся в статусе редактирования черновика.
Совместимость с браузером
Картина показываетPerformance Timeline Level 2
серединаPerformanceObserver
Поддержка API:
Performance Timeline Level 2
В практических приложениях в основном используетсяPerformanceObserver
API.
Картина показываетUser Timing
поддержки:
Как используются два?
начальный уровень
Если у вас есть относительно дорогая функцияfoo
, Вы любопытны в разных браузерах, выполнитеfoo
Сколько времени требуется, тогда вы можете сделать:
const prefix = fix => input => `${fix}${input}`
const prefixStart = prefix('start')
const prefixEnd = prefix('end')
const measure = (fn, name = fn.name) => {
performance.mark(prefixStart(name))
fn()
performance.mark(prefixEnd(name))
}
В приведенном выше коде используется новый API:performance.mark
.
По стандарту звонитеperformance.mark(markName)
, происходят следующие шаги:
- создать новый
PerformanceMark
объект (далее запись); - Буду
name
свойство установлено наmarkName
; - Буду
entryType
свойство установлено на'mark'
; - Буду
startTime
свойство установлено наperformance.now()
значение; - Буду
duration
свойство установлено на0
; - поставить запись в очередь;
- добавить элемент в
performance entry buffer
середина; - вернуть
undefined
По смыслу «поставить в очередь», пожалуйста, смотритеЯ 3 из .GitHub.IO/performance… [1]в очереди
PerformanceEntry
Вышеупомянутый процесс можно просто понять так: «Пожалуйста, обозреватель запишет файл с именемmarkName
запись выступления».
После этого мы можем пройтиperformance.getEntriesByType
Получить конкретные данные:
const getMarks = key => {
return performance
.getEntriesByType('mark') // 只获取通过 performance.mark 记录的数据
.filter(({ name }) => name === prefixStart(key) || name === prefixEnd(key))
}
const getDuration = entries => {
const { start = 0, end = 0 } = entries.reduce((last, { name, startTime }) => {
if (/^start/.test(name)) {
last.start = startTime
} else if (/^end/.test(name)) {
last.end = startTime
}
return last
})
return end - start
}
const retrieveResult = key => getDuration(getMarks(key))
performance.getEntriesByType('mark')
что получаетсяmark
сгенерированные данные.
"Получение данных, зачем много кода? Особенно
getDuration
, часть, которая различает время начала и окончания, слишком тривиальна! ? "
Команда производительности W3C, предвидя некоторые жалобы, пошла еще дальше.performance.measure
метод~
performance.measure
Метод получает три параметра, чтобыmeasureName
,startMark
так же какendMark
.
startMark
а такжеendMark
Это легко понять, это соответствует началу и концуmarkName
.measureName
для каждогоmeasure
Поведение, обеспечивающее личность.
После звонка,performance.measure
будет основываться наstartMark
а такжеendMark
Соответствующие две записи (обе поperformance.mark
производить), образуяentryType
для'measure'
новых записей и автоматически рассчитывает время выполнения.
конкретные шаги, которые происходят за кулисами, иperformance.mark
Очень похоже, заинтересованные читатели могут обратиться к разделу 3.1.3 спецификации.Woohoo. Я 3.org/TR/user-topic… [2].
использоватьperformance.measure
Рефакторинг предыдущего кода:
const measure = (fn, name = fn.name) => {
const startName = prefixStart(name)
const endName = prefixEnd(name)
performance.mark(startName)
fn()
performance.mark(endName)
// 调用 measure
performance.measure(name, startName, endName)
}
const getDuration = entries => {
// 直接获取 duration
const [{ duration }] = entries
return duration
}
const retrieveResult = key => getDuration(performance.getEntriesByName(key))
// 使用时
function foo() {
// some code
}
measure(foo)
const duration = retrieveResult('foo')
console.log('duration of foo is:', duration)
как? Это яснее и короче?
Здесь мы непосредственно переходим
performance.getEntriesByName(measureName)
форму, полученнуюmeasure
сгенерированные данные.
асинхронная функция
Асинхронная функция?async
await
Приходите на набор:
const asyncMeasure = async (fn, name = fn.name) => {
const startName = prefixStart(name)
const endName = prefixEnd(name)
performance.mark(startName)
await fn()
performance.mark(endName)
// 调用 measure
performance.measure(name, startName, endName)
}
рассмотрение
mark |
measure |
|
---|---|---|
эффект | Когда операция выполняется, запишите метку времени | для начала + концаmark значения, агрегированные для формирования непосредственно используемых данных о производительности |
недостаточный | Для операции требуются две временные метки для измерения производительности. | Если вы хотите измерить несколько операций, вам нужно вызывать их несколько раз. |
Вышеупомянутые связанные API все изUser Timing Level 2
. при присоединенииPerformance Timeline
После этого мы можем оптимизировать структуру кода~
Расширенное издание
Как было сказано выше, каждый раз, когда вы хотите увидеть производительность, она как бы вызывается активноretrieveResult
функция. Один-два раза — это нормально, но чаще это, несомненно, увеличит повторяющийся код, что нарушает принцип DRY.
существуетPerformance Timeline Level 2
, рабочая группа добавила новыеPerformanceObserver
Интерфейс предназначен для решения следующих трех задач:
- Каждый раз, когда вы просматриваете данные, следует взять на себя инициативу вызова интерфейса;
- При получении различных типов индикаторов данных генерируется повторяющаяся логика;
- Другие ресурсы должны работать одновременно
performance buffer
, возникает ситуация конкуренции ресурсов.
Для интерфейсных инженеров фактическое использование — это просто изменение API.
Еще предстоит измерить производительность операции, в поддержкуPerformance Timeline Level 2
браузер, вы можете написать:
const observer = new PerformanceObserver(list =>
list.getEntries().map(({ name, startTime }) => {
// 如何利用数据的逻辑
console.log(name, startTime)
return startTime
})
)
observer.observe({
entryTypes: ['mark'],
buffered: true
})
Умный телефон должен найти некоторые изменения:
- использовал
getEntries
вместоgetEntriesByType
; - передача
observe
метод, наборentryTypes
а такжеbuffer
потому что звонюobserve
метод установки желаемого наблюденияentryTypes
, так что не надо звонитьgetEntriesByType
.
buffered
Значение поля состоит в том, следует лиobserver
изbuffer
добавить эту запись (buffer
), значение по умолчаниюfalse
.
о том, почему
buffered
настройки, заинтересованные читатели могут обратиться кGitHub.com/my3from/perform… [3]
PerformanceObserver
Конструктор
оглядыватьсяPerformanceObserver
.
При создании экземпляра он получает параметр с именемPerformanceObserverCallback
, как следует из названия, является функцией обратного вызова.
Функция имеет два параметра, которыеPerformanceObserverEntryList
а такжеPerformanceObserver
. Первый — это сбор данных о производительности, которые нас интересуют. На самом деле мы видели это несколько раз, например.performance.getEntriesByType('navigation')
Этот тип данных будет возвращен; последний представляет собой конкретизированный объект, который можно понимать как функцию, предоставляющуюthis
стоимость.
Все специфические операции, связанные с данными, такие как создание отчетов, печать и т. д., могут бытьPerformanceObserverCallback
в ходе выполнения.
После создания возвращаетobserver
объект. Этот объект имеет два ключевых метода, а именноobserve
а такжеdisconnect
.
-
observe
Используется, чтобы сообщить браузеру: «Я хочу наблюдать за этими типами данных о производительности»; -
disconnect
Используется для отключения наблюдения, пустойbuffer
.
почему бы не быть
disconnect
метод? Несколько ироничный факт заключается в том, что непрерывное наблюдение за данными о производительности в течение длительного времени является поведением, требующим относительно высокой производительности. Поэтому лучше прекратить наблюдение и очистить соответствующийbuffer
, производительность выпуска.
использоватьPerformanceObserver
+ performance.measure
Рефакторинг предыдущего кода:
// 在 measure.js 中
const getMeasure = () => {
const observer = new PerformanceObserver(list => {
list.getEntries().forEach(({ name, duration }) => {
console.log(name, duration)
// 操作数据的逻辑
})
})
// 只需要关注 measure 的数据
observer.observe({
entryTypes: ['measure'],
buffered: true
})
return observer
}
// 项目入口文件的顶部
let observer
if (window.PerformanceObserver) {
observer = getMeasure()
}
// 某一个合适的时间 不再需要监控性能了
if (observer) {
observer.disconnect()
}
Таким образом, операция получения данных о производительности реализует DRY.
Меры предосторожности
Если вы уже готовитесь перед экраном, хотите попробовать, а затем сделать перерыв, обратите внимание на следующие меры предосторожности:
- Возможности мониторинга производительности, предоставляемые двумя стандартами, ограничены не только интерфейсным кодом, но и сложными асинхронными интерфейсами.
async
+await
Мониторинг «производительности интерфейса» в форме (акцент на производительность, воспринимаемую пользователем, поскольку на производительность интерфейса могут существенно влиять сетевое окружение, использование кеша, прокси-серверы и т. д.); - Если вы хотите сообщить данные, вам нужно подумать о том, нужно ли полностью отправлять соответствующий код? Лучшим способом может быть: (на основе
User Agent
) оттенки серого; - не функция
measure
один раз. Следует сосредоточиться на сценариях, в которых могут возникнуть узкие места в производительности. И только когда узкое место действительно возникнет, попробуйте снова составить отчет о данных; - Цель этой статьи не в том, чтобы заменить различные библиотеки эталонных тестов. На самом деле базовое тестирование производительности по-прежнему должно выполняться библиотекой эталонных тестов. В этой статье основное внимание уделяется «мониторингу», то есть пользовательскому опыту, и обнаружению реальных сценариев узких мест в производительности.
Суммировать
Performance Timeline
+ User Timing
Он предоставляет интерфейсным разработчикам инструмент для измерения производительности кода. Если проект, с которым вы столкнулись, «чувствителен к производительности», начните пробовать его как можно скорее~
Спасибо
Гао Фэн, Хуан Сяолу, Лю Боуэн, Ли Сунфэн и другие выдвинули ценные ревизионные мнения по структуре и деталям статьи, которые расположены в последовательном порядке (хронологическом порядке).
Ссылка в тексте
О Еженедельнике Циу
«Qiwu Weekly» — это сообщество передовых технологий, управляемое профессиональной командой «Qiwu Troupe» компании 360. Подписавшись на официальный аккаунт, вы можете отправить нам статью, отправив ссылку прямо на фон.