Méthodes d'amélioration et d'optimisation des performances dans les applications React
Publié: 2022-03-10React permet aux applications Web de mettre à jour rapidement leurs interfaces utilisateur (UI), mais cela ne signifie pas que votre application React moyenne ou grande fonctionnera efficacement. Ses performances dépendront de la façon dont vous utilisez React lors de sa construction, et de votre compréhension du fonctionnement de React et du processus par lequel les composants traversent les différentes phases de leur cycle de vie. React offre de nombreuses améliorations de performances à une application Web, et vous pouvez obtenir ces améliorations grâce à diverses techniques, fonctionnalités et outils.
Dans ce didacticiel, nous aborderons diverses méthodes d'optimisation des performances dans les applications React, ainsi que les fonctionnalités de React que nous pouvons utiliser pour améliorer les performances.
Par où commencer pour optimiser les performances dans une application React ?
Nous ne pouvons pas commencer à optimiser une application sans savoir exactement quand et où optimiser. Vous vous demandez peut-être : « Par où commencer ?
Au cours du processus de rendu initial, React construit une arborescence DOM de composants. Ainsi, lorsque les données changent dans l'arborescence DOM, nous voulons que React restitue uniquement les composants qui ont été affectés par le changement, en ignorant les autres composants de l'arborescence qui n'ont pas été affectés.
Cependant, React pourrait finir par restituer tous les composants de l'arborescence DOM, même si tous ne sont pas affectés. Cela entraînera un temps de chargement plus long, une perte de temps et même une perte de ressources CPU. Nous devons empêcher que cela se produise. C'est donc là que nous concentrerons nos efforts d'optimisation.
Dans cette situation, nous pourrions configurer chaque composant pour ne rendre ou différencier que lorsque cela est nécessaire, afin d'éviter de gaspiller des ressources et du temps.
Mesurer les performances
Ne démarrez jamais le processus d'optimisation de votre application React en fonction de ce que vous ressentez. Utilisez plutôt les outils de mesure disponibles pour analyser les performances de votre application React et obtenez un rapport détaillé de ce qui pourrait la ralentir.
Analyser les composants React avec l'onglet Performances de Chrome
Selon la documentation de React, pendant que vous êtes encore en mode développement, vous pouvez utiliser l'onglet "Performance" du navigateur Chrome pour visualiser comment les composants React montent, mettent à jour et démontent. Par exemple, l'image ci-dessous montre l'onglet "Performance" de Chrome profilant et analysant mon blog en mode développement.
Pour le faire, suivez ces étapes:
- Désactivez temporairement toutes les extensions, en particulier React Developer Tools, car elles peuvent perturber le résultat de l'analyse. Vous pouvez facilement désactiver les extensions en exécutant votre navigateur en mode incognito.
- Assurez-vous que l'application s'exécute en mode développement. Autrement dit, l'application doit être exécutée sur votre hôte local.
- Ouvrez les outils de développement de Chrome, cliquez sur l'onglet "Performance", puis cliquez sur le bouton "Enregistrer".
- Effectuez les actions que vous souhaitez profiler. N'enregistrez pas plus de 20 secondes, sinon Chrome pourrait se bloquer.
- Arrêtez l'enregistrement.
- Les événements React seront regroupés sous le label "User Timing".
Les chiffres du profileur sont relatifs. La plupart des temps et des composants seront rendus plus rapidement en production. Néanmoins, cela devrait vous aider à déterminer quand l'interface utilisateur est mise à jour par erreur, ainsi que la profondeur et la fréquence des mises à jour de l'interface utilisateur.
Profileur d'outils de développement React
Selon la documentation de React, dans react react-dom
16.5+ et react react-native
0.57+, des capacités de profilage améliorées sont disponibles en mode développeur à l'aide de React Developer Tools Profiler. Le profileur utilise l'API Profiler expérimentale de React pour rassembler des informations de synchronisation sur chaque composant rendu, afin d'identifier les goulots d'étranglement de performances dans une application React.
Téléchargez simplement React Developer Tools pour votre navigateur, puis vous pourrez utiliser l'outil de profilage qui l'accompagne. Le profileur ne peut être utilisé qu'en mode développement ou dans la version de profilage de production de React v16.5+. L'image ci-dessous est le résumé du profileur de mon blog en mode développement à l'aide de React Developer Tools Profiler :
Pour y parvenir, suivez ces étapes :
- Téléchargez les outils de développement React.
- Assurez-vous que votre application React est soit en mode développement, soit dans la version de profilage de production de React v16.5+.
- Ouvrez l'onglet "Outils de développement" de Chrome. Un nouvel onglet nommé "Profiler" sera disponible, fourni par React Developer Tools.
- Cliquez sur le bouton "Enregistrer" et effectuez les actions que vous souhaitez profiler. Idéalement, arrêtez l'enregistrement après avoir effectué les actions que vous souhaitez profiler.
- Un graphique (appelé flamegraph) apparaîtra avec tous les gestionnaires d'événements et composants de votre application React.
Remarque : Consultez la documentation pour plus d'informations.
Mémoïsation avec React.memo()
React v16 a été publié avec une API supplémentaire, un composant d'ordre supérieur appelé React.memo()
. Selon la documentation, cela n'existe qu'en tant qu'optimisation des performances .
Son nom, « memo », vient de la mémorisation, qui est essentiellement une forme d'optimisation utilisée principalement pour accélérer le code en stockant les résultats des appels de fonctions coûteux et en renvoyant le résultat stocké chaque fois que la même fonction coûteuse est appelée à nouveau.
La mémorisation est une technique permettant d'exécuter une fonction une seule fois, généralement une fonction pure, puis d'enregistrer le résultat en mémoire. Si nous essayons d'exécuter à nouveau cette fonction, avec les mêmes arguments qu'auparavant , elle renverra simplement le résultat précédemment enregistré de l'exécution de la première fonction, sans exécuter à nouveau la fonction.
En mappant la description ci-dessus à l'écosystème React, les fonctions mentionnées sont des composants React et les arguments sont des accessoires.
Le comportement par défaut d'un composant déclaré à l'aide React.memo()
est qu'il ne s'affiche que si les accessoires du composant ont changé. Il effectue une comparaison superficielle des accessoires pour vérifier cela, mais une option est disponible pour remplacer cela.
React.memo()
améliore les performances d'une application React en évitant de restituer les composants dont les accessoires n'ont pas changé ou lorsque le re-rendu n'est pas nécessaire.
Le code ci-dessous est la syntaxe de base de React.memo()
:
const MemoizedComponent = React.memo((props) => { // Component code goes in here })
Quand utiliser React.memo()
- Composant fonctionnel pur
Vous pouvez utiliserReact.memo()
si votre composant est fonctionnel, reçoit les mêmes accessoires et affiche toujours la même sortie. Vous pouvez également utiliserReact.memo()
sur des composants fonctionnels non purs avec des hooks React. - Le composant s'affiche souvent
Vous pouvez utiliserReact.memo()
pour envelopper un composant qui s'affiche souvent. - Le composant se restitue avec les mêmes accessoires
UtilisezReact.memo()
pour envelopper un composant qui est généralement fourni avec les mêmes accessoires lors du nouveau rendu. - Éléments moyens à élevés
Utilisez-le pour un composant qui contient un nombre moyen à élevé d'éléments d'interface utilisateur pour vérifier l'égalité des accessoires.
Remarque : Soyez prudent lorsque vous mémorisez des composants qui utilisent des accessoires comme rappels. Assurez-vous d'utiliser la même instance de fonction de rappel entre les rendus. En effet, le composant parent peut fournir différentes instances de la fonction de rappel à chaque rendu, ce qui entraînera l'interruption du processus de mémorisation. Pour résoudre ce problème, assurez-vous que le composant mémorisé reçoit toujours la même instance de rappel.
Voyons comment nous pouvons utiliser la mémorisation dans une situation réelle. Le composant fonctionnel ci-dessous, appelé "Photo", utilise React.memo()
pour empêcher le re-rendu.
export function Photo({ title, views }) { return ( <div> <div>Photo title: {title}</div> <div>Location: {location}</div> </div> ); } // memoize the component export const MemoizedPhoto = React.memo(Photo);
Le code ci-dessus consiste en un composant fonctionnel qui affiche un div contenant un titre de photo et l'emplacement du sujet sur la photo. Nous mémorisons également le composant en créant une nouvelle fonction et en l'appelant MemoizedPhoto
. La mémorisation du composant photo empêchera le composant d'être rendu à nouveau tant que les accessoires, le title
et l' location
sont les mêmes lors des rendus suivants.
// On first render, React calls MemoizedPhoto function. <MemoizedPhoto title="Effiel Tower" location="Paris" /> // On next render, React does not call MemoizedPhoto function, // preventing rendering <MemoizedPhoto title="Effiel Tower" location="Paris" />
Ici, React appelle la fonction mémorisée une seule fois. Il ne rendra pas le composant lors du prochain appel tant que les accessoires restent les mêmes.
Regroupement et minification
Dans les applications d'une seule page React, nous pouvons regrouper et réduire tout notre code JavaScript dans un seul fichier. C'est OK, tant que notre application est relativement petite.
Au fur et à mesure que notre application React se développe, regrouper et réduire tout notre code JavaScript dans un seul fichier devient problématique, difficile à comprendre et fastidieux. Cela affectera également les performances et le temps de chargement de notre application React car nous envoyons un gros fichier JavaScript au navigateur. Nous avons donc besoin d'un processus pour nous aider à diviser la base de code en différents fichiers et à les transmettre au navigateur à intervalles réguliers, selon les besoins.
Dans une situation comme celle-ci, nous pouvons utiliser une forme de regroupement d'actifs comme Webpack, puis tirer parti de sa fonctionnalité de fractionnement de code pour diviser notre application en plusieurs fichiers.
Le fractionnement de code est suggéré dans la documentation de Webpack comme moyen d'améliorer le temps de chargement d'une application. Il est également suggéré dans la documentation de React pour le chargement différé (ne servant que les éléments actuellement nécessaires à l'utilisateur), ce qui peut considérablement améliorer les performances.
Webpack suggère trois approches générales pour le fractionnement de code :
- Points d'entrée
Diviser manuellement le code à l'aide de la configuration d'entrée. - Prévention des doublons
UtilisezSplitChunksPlugin
pour dédupliquer et diviser des morceaux. - Importations dynamiques
Diviser le code via des appels de fonction en ligne dans les modules.
Avantages du fractionnement de code
- Le fractionnement du code facilite les ressources de cache du navigateur et le code qui ne change pas souvent.
- Il aide également le navigateur à télécharger des ressources en parallèle, ce qui réduit le temps de chargement global de l'application.
- Cela nous permet de diviser le code en morceaux qui seront chargés à la demande ou selon les besoins de l'application.
- Il maintient le téléchargement initial des ressources lors du premier rendu relativement petit, réduisant ainsi le temps de chargement de l'application.
Structures de données immuables
La documentation de React parle du pouvoir de ne pas faire muter les données. Toutes les données qui ne peuvent pas être modifiées sont immuables. L'immuabilité est un concept que les programmeurs React doivent comprendre.
Une valeur ou un objet immuable ne peut pas être modifié. Ainsi, lorsqu'il y a une mise à jour, une nouvelle valeur est créée en mémoire, laissant l'ancienne intacte.
Nous pouvons utiliser des structures de données immuables et React.PureComponent
pour vérifier automatiquement un changement d'état complexe. Par exemple, si l'état de votre application est immuable, vous pouvez en fait enregistrer tous les objets d'état dans un seul magasin avec une bibliothèque de gestion d'état comme Redux, ce qui vous permet d'implémenter facilement les fonctionnalités d'annulation et de rétablissement.
N'oubliez pas que nous ne pouvons pas modifier les données immuables une fois qu'elles sont créées.
Avantages des structures de données immuables
- Ils n'ont pas d'effets secondaires.
- Les objets de données immuables sont faciles à créer, à tester et à utiliser.
- Ils nous aident à écrire une logique qui peut être utilisée pour vérifier rapidement les mises à jour dans l'état, sans avoir à vérifier les données encore et encore.
- Ils aident à empêcher le couplage temporel (un type de couplage dans lequel le code dépend de l'ordre d'exécution).
Les bibliothèques suivantes aident à fournir un ensemble de structures de données immuables :
- assistant d'immuabilité
Muter une copie de données sans changer la source. - Immuable.js
Les collectes de données persistantes immuables pour JavaScript augmentent l'efficacité et la simplicité. - transparente-immuable
Les structures de données immuables pour JavaScript deviennent rétrocompatibles avec les tableaux et objets JavaScript normaux. - Réagir-copier-écrire
Cela donne un état immuable avec une API mutable.
Autres méthodes d'amélioration des performances
Utiliser une version de production avant le déploiement
La documentation de React suggère d'utiliser la version de production minifiée lors du déploiement de votre application.
Évitez les fonctions anonymes
Étant donné que les fonctions anonymes ne se voient pas attribuer d'identifiant (via const/let/var
), elles ne sont pas persistantes chaque fois qu'un composant est inévitablement rendu à nouveau. Cela amène JavaScript à allouer une nouvelle mémoire chaque fois que ce composant est restitué, au lieu d'allouer un seul morceau de mémoire une seule fois, comme lorsque des fonctions nommées sont utilisées.
import React from 'react'; // Don't do this. class Dont extends Component { render() { return ( <button onClick={() => console.log('Do not do this')}> Don't </button> ); } } // The better way class Do extends Component { handleClick = () => { console.log('This is OK'); } render() { return ( <button onClick={this.handleClick}> Do </button> ); } }
Le code ci-dessus montre deux manières différentes de faire en sorte qu'un bouton exécute une action lors d'un clic. Le premier bloc de code utilise une fonction anonyme dans la onClick()
, ce qui affecterait les performances. Le deuxième bloc de code utilise une fonction nommée dans la fonction onClick()
, ce qui est la bonne méthode dans ce scénario.
Le montage et le démontage des composants coûtent souvent cher
L'utilisation de conditionnels ou de tenaires pour faire disparaître un composant (c'est-à-dire le démonter) n'est pas conseillée, car le composant fait disparaître entraînera le repaint et le reflow du navigateur. Il s'agit d'un processus coûteux car les positions et les géométries des éléments HTML dans le document devront être recalculées. Au lieu de cela, nous pouvons utiliser les propriétés d' opacity
et de visibility
de CSS pour masquer le composant. De cette façon, le composant sera toujours dans le DOM mais invisible, sans aucun coût de performance.
Virtualiser les longues listes
La documentation suggère que si vous affichez une liste avec une grande quantité de données, vous devez afficher une petite partie des données de la liste à la fois dans la fenêtre visible. Ensuite, vous pouvez restituer plus de données au fur et à mesure que la liste défile ; par conséquent, les données ne sont affichées que lorsqu'elles se trouvent dans la fenêtre. Ce processus est appelé "fenêtrage". Dans le fenêtrage, un petit sous-ensemble de lignes est rendu à un moment donné. Il existe des bibliothèques populaires pour ce faire, dont deux sont maintenues par Brian Vaughn :
- fenêtre de réaction
- réagir-virtualisé
Conclusion
Il existe plusieurs autres méthodes pour améliorer les performances de votre application React. Cet article a discuté des méthodes les plus importantes et les plus efficaces d'optimisation des performances.
J'espère que vous avez apprécié la lecture de ce tutoriel. Vous pouvez en savoir plus via les ressources répertoriées ci-dessous. Si vous avez des questions, laissez-les dans la section des commentaires ci-dessous. Je serai heureux de répondre à chacun d'entre eux.
Références et ressources connexes
- "Optimisation des performances", React Docs
- "Utilisez React.memo à bon escient", Dmitri Pavlutin
- "Techniques d'optimisation des performances dans React", Niteesh Yadav
- "Immutabilité dans React : il n'y a rien de mal à faire muter des objets", Esteban Herrera
- "10 façons d'optimiser les performances de votre application React", Chidume Nnamdi
- "5 conseils pour améliorer les performances de vos applications React", William Le