Suivi des performances d'une application React en production
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
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.
Questions fréquentes
Qu'est-ce qui rend une application React performante ?
Pour optimiser le rendu de React, vous devez vous assurer que les composants ne reçoivent que les props nécessaires. Cela vous permettra de contrôler la consommation du CPU et d'éviter de sur-rendre des fonctionnalités inutiles. La solution consiste à créer un composant fonctionnel qui collectera tous les props et les redistribuera aux autres composants.