Поскольку мы не можем заставить каждого пользователя установить React DevTools
и профилировать приложение, когда они взаимодействуют с ним, было бы неплохо, если бы мы могли каким-то образом отследить некоторое время рендеринга и отправить эту информацию на наши серверы для мониторинга.
Существуют решения для мониторинга и измерения производительности приложения независимо от того, какую платформу ты используешь (Lighthouse CI особенно интересен). Тем не менее, команда React
создала API специально для измерения производительности компонентов React
в продакшн. Он не даёт нам такого объема информации, как React DevTools
, но некоторая полезность определенно есть, которая помогает определить причины проблем с производительностью.
ПРИМЕЧАНИЕ: чтобы всё это работало в продакшн среде, тебе необходимо включить сборку профайлера React. Это приведет к небольшому замедлению производительности, поэтому facebook.com
предоставляет профайлер сборку своего приложения только небольшому числу пользователей. Узнай больше о том, как включить Profile в React Profiler API.
Вот базовый пример использования компонента React <Profiler />
:
<App>
<Profiler id="Navigation" onRender={onRenderCallback}>
<Navigation />
</Profiler>
<Main />
</App>
Функция onRenderCallback
вызывается со следующими аргументами:
function onRenderCallback(
id, // "id" дерева профилировщика, который только что совершился
phase, // либо "mount" (если дерево только что собралось), либо "update" (если оно перерисовывается)
actualDuration, // время, потраченное на рендеринг совершенного обновления
baseDuration, // расчетное время рендеринга всего поддерева без memoization
startTime, // когда React начал рендеринг этого обновления
commitTime, // когда React совершил это обновление
interactions, // Набор (Set) взаимодействий, принадлежащих этому обновлению
) {
// Aggregate or log render timings...
}
Более подробно в документации React.
Важно отметить, что если ты не создашь свое приложение с использованием react-dom/profiling
и scheduler/tracing-profiling
, этот компонент ничего не сделает. Ты можешь узнать, как его настроить из моего поста React Profiler API.
Отсюда ты можешь отправить данные onRenderCallback
в инструмент мониторинга (например, Grafana). Поскольку повторный ре-рендеринг может происходить ОЧЕНЬ часто, лично я бы предложил собирать их и отправлять вместе каждые 5 секунд. Например:
let queue = [];
// отправлять sendProfileQueue каждые 5 секунд
setInterval(sendProfileQueue, 5000);
function onRenderCallback(
id,
phase,
actualDuration,
baseDuration,
startTime,
commitTime,
interactions,
) {
queue.push({
id,
phase,
actualDuration,
baseDuration,
startTime,
commitTime,
interactions,
});
}
function sendProfileQueue() {
if (!queue.length) {
return Promise.resolve();
}
const queueToSend = [...queue];
queue = [];
// вот где мы на самом деле делаем вызов на сервер,
// для отправки данных `queueToSend` на наш бэкэнд.
console.info("sending profile queue", queueToSend);
return Promise.resolve();
}
Следует помнить, что, поскольку, это продакшн сборка, React
старается не снижать производительность при его измерении (что весьма разумно). Из-за этого мы ограничены в информации, которую можем получить. Таким образом, стратегически логично разместить компоненты <Profiler />
в своем приложении с понятными идентификаторами, чтобы ты мог легко определить источник проблемы с производительностью.
Также обрати внимание:
<App>
<Profiler id="Navigation" onRender={onRenderCallback}>
<Navigation />
</Profiler>
<Profiler id="Main" onRender={onRenderCallback}>
<Main>
<LeftNav />
<Profiler id="Content" onRender={onRenderCallback}>
<Content />
</Profiler>
<RightNav />
</Main>
</Profiler>
</App>
В этом случае, если <Content />
делает ре-рендеринг, будет вызван Content
и Main
profiler, но не Navigation
. Если <LeftNav />
получит повторный рендеринг, будет вызван Main
, но не Content
или Navigation
.
Вот пример того, как выглядят данные:
{
id: "Navbar",
phase: "update",
actualDuration: 0.00500003807246685,
baseDuration: 0.00500003807246685,
startTime: 8463.875000015832,
commitTime: 8481.985000020359,
interactions: [ // на самом деле Set, а не массив
{
__count: 0
id: 3,
name: "menu click",
timestamp: 8481.93499999251,
}
],
}
Ты можешь узнать больше об этом (экспериментальном) взаимодействии здесь.
Внедрение этих данных в программное обеспечение для мониторинга может помочь тебе найти некоторые интересные тенденции и определить регрессию производительности.
Удачи!