Главная Категории Контакты Поиск

React performance monitoring

Как настроить мониторинг производительности для продакшн приложений React.

React·01.07.2020·читать 2 мин 🤓·Автор: Alexey Myzgin

Поскольку мы не можем заставить каждого пользователя установить 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,
    }
  ],
}

Ты можешь узнать больше об этом (экспериментальном) взаимодействии здесь.

Внедрение этих данных в программное обеспечение для мониторинга может помочь тебе найти некоторые интересные тенденции и определить регрессию производительности.

Удачи!

Website, name & logo
Copyright © 2022. Alex Myzgin