Nous devrions toujours proposer des expériences rapides à nos utilisateurs, mais il arrive que quelque chose échappe à notre processus de révision des pull request et que nos utilisateurs commencent à avoir une expérience lente. À moins qu'ils ne se plaignent à nous, nous n'avons souvent aucun moyen de savoir que les choses vont si lentement pour eux. Les plaintes des utilisateurs ne constituent pas une excellente politique de contrôle de la qualité.

Cet article est une traduction d'un article de Kent C. Dodds. React Production Performance
performance

Comme nous ne pouvons pas demander à chaque utilisateur d'installer React DevTools et de profiler l'application pour nous au fur et à mesure qu'il interagit avec elle, il serait bon que nous puissions, d'une manière ou d'une autre, suivre certains des temps de rendu et envoyer ces informations à nos serveurs pour que nous puissions les contrôler.

Il existe des solutions pour surveiller et mesurer les performances de votre application, quel que soit le framework que vous utilisez (Lighthouse CI est particulièrement intéressant). Cela dit, l'équipe React a créé une API spécifiquement destinée à mesurer les performances de vos composants React en production. Elle ne nous donne pas autant d'informations que les React DevTools, mais elle nous donne des informations utiles qui vous aideront à déterminer où se situent les problèmes de performance.

REMARQUE : pour que tout cela fonctionne en production, vous devez activer la construction du profileur de React. Cette opération a un léger coût en termes de performances, c'est pourquoi facebook.com ne propose la version profilée de son application qu'à un sous-ensemble d'utilisateurs. Pour en savoir plus sur la façon d'activer la construction du profileur, consultez la rubrique Profile a React App for Performance.

Comment utiliser le composant profiler ?

Voici un exemple d'utilisation simple du composant <Profiler />

<App>
  <Profiler id="Navigation" onRender={onRenderCallback}>
    <Navigation />
  </Profiler>
  <Main />
</App>

La fonction onRenderCallback est appellée avec les arguments suivants

function onRenderCallback(
  id, // the "id" prop of the Profiler tree that has just committed
  phase, // either "mount" (if the tree just mounted) or "update" (if it re-rendered)
  actualDuration, // time spent rendering the committed update
  baseDuration, // estimated time to render the entire subtree without memoization
  startTime, // when React began rendering this update
  commitTime, // when React committed this update
  interactions, // the Set of interactions belonging to this update
) {
  // Aggregate or log render timings...
}

Il est important de noter que si vous ne construisez pas votre application en utilisant react-dom/profiling et scheduler/tracing-profiling, ce composant ne fera rien. Vous pouvez apprendre comment les configurer dans mon article de blog Profile a React App for Performance.

À partir de là, vous voudrez envoyer les données onRenderCallback à un outil de surveillance (comme Grafana, par exemple). Comme les rendus peuvent être très nombreux, je vous suggère de les regrouper et de les envoyer toutes les 5 secondes environ. Par exemple :

let queue = []

// sendProfileQueue every 5 seconds
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 = []
  // here's where we'd actually make the server call to send the queueToSend
  // data to our backend...
  console.info('sending profile queue', queueToSend)
  return Promise.resolve()
}

Il faut garder à l'esprit que, comme il s'agit d'une application en production, React fait de son mieux pour ne pas nuire aux performances en la mesurant (ce qui est plutôt raisonnable). De ce fait, nous sommes limités dans les informations que nous pouvons recevoir. Ainsi, vous voudrez probablement placer stratégiquement des composants <Profiler /> dans votre application avec des props d'identification sensibles afin de pouvoir déterminer la source du problème de performance plus facilement.

Imbriquer des profilers

Notez également que vous pouvez les imbriquer :

<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>

Dans ce cas, si <Content /> obtenait un rerendu, les onRenderCallbacks des profileurs Content et Main seraient appelés (pas celui de Navigation). Si <LeftNav /> obtenait un rerendu, alors Main serait appelé, mais pas Content ou Navigation.

Voici un exemple de ce à quoi ressemblent les données :

{
  id: "Navigation",
  phase: "update",
  actualDuration: 0.09999994654208422,
  baseDuration: 0.3799999540206045,
  startTime: 104988.11499998556,
  commitTime: 104988.45000000438,
  interactions: [ // this is actually a Set, not an array
    {
      __count: 0
      id: 3,
      name: "menu click",
      timestamp: 104978.33499999251,
    }
  ],
}

En introduisant ces données dans un logiciel de surveillance, vous pourrez trouver des tendances intéressantes et repérer les régressions (pics) de performances.

Bonne chance ! Bon profilage.