Comment démarrer rapidement avec VUE3.0 : Prise en main
Dans ce tutoriel, nous apprendrons comment implémenter la mémoisation dans React. La mémorisation améliore les performances en mettant en cache les résultats des appels de fonction et en renvoyant ces résultats mis en cache lorsqu'ils sont à nouveau nécessaires.
Nous aborderons les points suivants :
Cet article suppose que vous avez une compréhension de base des composants de classe et de fonction dans React.
Si vous souhaitez consulter ces sujets, vous pouvez consulter les composants et accessoires officiels de la documentation React
https://reactjs.org/docs/components-and-props.html
Avant de discuter des détails de la mémorisation dans React, voyons d'abord comment React utilise le DOM virtuel pour restituer l'interface utilisateur.
Un DOM standard contient essentiellement un ensemble de nœuds présentés sous la forme d’un arbre. Chaque nœud du DOM représente un élément de l'interface utilisateur. Chaque fois qu'un changement d'état se produit dans l'application, les nœuds correspondants à cet élément d'interface utilisateur et à tous ses éléments enfants sont mis à jour dans l'arborescence DOM, ce qui déclenche ensuite un redessin de l'interface utilisateur.
Avec l'aide d'algorithmes d'arborescence DOM efficaces, la mise à jour des nœuds est plus rapide, mais le redessin est lent et peut avoir un impact sur les performances lorsque le DOM comporte un grand nombre d'éléments d'interface utilisateur. Par conséquent, le DOM virtuel a été introduit dans React.
Il s'agit d'une représentation virtuelle du vrai DOM. Désormais, chaque fois qu'il y a un changement dans l'état de l'application, React ne met pas directement à jour le DOM réel, mais crée un nouveau DOM virtuel. React comparera ensuite ce nouveau DOM virtuel avec le DOM virtuel créé précédemment, trouvera les différences (Note du traducteur : c'est-à-dire trouvera les nœuds qui doivent être mis à jour), puis redessinera.
Sur la base de ces différences, le DOM virtuel peut mettre à jour le DOM réel plus efficacement. Cela améliore les performances car le DOM virtuel ne met pas simplement à jour l'élément d'interface utilisateur et tous ses éléments enfants, mais met effectivement à jour uniquement les modifications nécessaires et minimes dans le DOM réel.
Dans la section précédente, nous avons vu comment React utilise un DOM virtuel pour effectuer efficacement les opérations de mise à jour du DOM afin d'améliorer les performances. Dans cette section, nous présenterons un exemple qui explique la nécessité d'utiliser la mémoisation afin d'améliorer encore les performances.
Nous allons créer une classe parent qui contient un bouton qui incrémente une variable appelée count
. Le composant parent appelle également le composant enfant et lui transmet des paramètres. Nous avons également ajouté console.log()
dans la méthode render
:
//Parent.js la classe Parent étend React.Component { constructeur (accessoires) { super(accessoires); this.state = { nombre : 0 } ; } handleClick = () => { this.setState((prevState) => { return {count: prevState.count + 1 }; }); } ; rendre() { console.log("Rendu parent"); retour ( <div className="Application"> <button onClick={this.handleClick}>Incrémenter</button> <h2>{this.state.count></h2> <Nom de l'enfant={"joe"} /> </div> ); } } export default Parent ;
Le code complet de cet exemple peut être consulté sur CodeSandbox.
Nous allons créer une classe Child
qui accepte les paramètres passés par le composant parent et les affiche dans l'interface utilisateur :
//Child.js la classe Child étend React.Component { rendre() { console.log("Rendu enfant"); retour ( <div> <h2>{this.props.name></h2> </div> ); } } export default Child;
count
changera chaque fois que nous cliquons sur le bouton dans le composant parent. L'état ayant changé, la méthode render
du composant parent est exécutée.
Les paramètres transmis au composant enfant ne changent pas à chaque fois que le composant parent est restitué, le composant enfant ne doit donc pas être restitué. Cependant, lorsque nous exécutons le code ci-dessus et continuons à incrémenter count
, nous obtenons le résultat suivant :
Rendu parent Rendu enfant Rendu parent Rendu enfant Rendu parent Rendu enfant
Vous pouvez essayer l'exemple ci-dessus dans ce bac à sable et afficher la sortie de la console.
À partir du résultat, nous pouvons voir que lorsque le composant parent est restitué, le composant enfant est restitué même si les paramètres transmis au composant enfant restent inchangés. Cela amènera le DOM virtuel du composant enfant à effectuer une vérification différentielle par rapport au DOM virtuel précédent. Puisqu'il n'y a aucun changement dans nos composants enfants et que tous les accessoires restent inchangés lors du nouveau rendu, le vrai DOM ne sera pas mis à jour.
Le fait que le DOM réel ne soit pas mis à jour inutilement constitue certainement un avantage en termes de performances, mais nous pouvons voir qu'un nouveau DOM virtuel est créé et qu'une vérification des différences est effectuée même lorsqu'il n'y a aucun changement réel dans les composants enfants. Pour les petits composants React, ce coût en performances est négligeable, mais pour les gros composants, l'impact sur les performances peut être significatif. Pour éviter ce nouveau rendu et cette vérification différentielle du DOM virtuel, nous utilisons la mémorisation.
Dans le contexte des applications React, la mémorisation est un moyen par lequel chaque fois que le composant parent est restitué, le composant enfant n'est restitué que lorsque les accessoires dont il dépend changent. S'il n'y a aucun changement dans les accessoires dont dépend le composant enfant, il n'exécutera pas la méthode de rendu et renverra les résultats mis en cache. Étant donné que la méthode de rendu n'est pas exécutée, il n'y aura pas de création de DOM virtuel ni de vérification différentielle, ce qui entraînera des performances améliorées.
Voyons maintenant comment implémenter la mémoisation dans les composants de classe et de fonction pour éviter ce nouveau rendu inutile.
Pour implémenter la mémoisation dans un composant de classe, nous utiliserons React.PureComponent. React.PureComponent
implémente ShouldComponentUpdate(), qui effectue une comparaison superficielle state
et props
et ne restitue le composant React que si les accessoires ou l'état changent.
Modifiez le composant enfant en code comme ceci :
//Child.js class Child extends React.PureComponent { // Ici, nous changeons React.Component en React.PureComponent rendre() { console.log("Rendu enfant"); retour ( <div> <h2>{this.props.name></h2> </div> ); } } export default Child;
Le code complet de cet exemple est affiché dans ce bac à sable.
Le composant parent reste inchangé. Désormais, lorsque nous incrémentons count
dans le composant parent, le résultat dans la console ressemble à ceci :
Rendu parent Rendu enfant Rendu parent Rendu parent
Pour le premier rendu, il appelle la méthode render
du composant parent et du composant enfant.
Pour chaque nouveau rendu après count
croissant, seule la fonction render
du composant parent est appelée. Les composants enfants ne seront pas restitués.
Pour implémenter la mémorisation dans les composants de fonction, nous utiliserons React.memo(). React.memo()
est un composant d'ordre supérieur (HOC) qui effectue un travail similaire à PureComponent
pour éviter un nouveau rendu inutile.
Voici le code du composant fonction :
//Child.js fonction d'exportation Enfant (accessoires) { console.log("Rendu enfant"); retour ( <div> <h2>{props.name></h2> </div> ); } export default React.memo(Child); // Ici, nous ajoutons HOC au composant enfant pour implémenter Memoization
et convertissons également le composant parent en composant fonction, comme indiqué ci-dessous :
//Parent.js exporter la fonction par défaut Parent() { const [compte, setCount] = useState(0); const handleClick = () => { setCount(compte + 1); } ; console.log("Rendu parent"); retour ( <div> <button onClick={handleClick}>Incrémenter</button> <h2>{count></h2> <Nom de l'enfant={"joe"} /> </div> ); }
Le code complet de cet exemple peut être consulté dans ce bac à sable.
Désormais, lorsque nous incrémentons count
dans le composant parent, les éléments suivants seront affichés sur la console :
Rendu parent Rendu enfant Rendu parent Rendu parent
le rendu parentReact.memo()
Dans l'exemple ci-dessus, nous voyons que lorsque nous utilisons React.memo()
HOC sur un composant enfant, le composant enfant n'est pas restitué même si le composant parent est restitué.
Cependant, une petite chose à noter est que si nous passons une fonction en tant que paramètre au composant enfant, le composant enfant sera restitué même après avoir utilisé React.memo()
. Regardons un exemple de ceci.
Nous allons modifier le composant parent comme indiqué ci-dessous. Ici, nous ajoutons une fonction de gestionnaire et la passons en tant que paramètre au composant enfant :
//Parent.js exporter la fonction par défaut Parent() { const [compte, setCount] = useState(0); const handleClick = () => { setCount(compte + 1); } ; gestionnaire const = () => { console.log("handler"); // La fonction de gestionnaire ici sera transmise au composant enfant}; console.log("Rendu parent"); retour ( <div className="Application"> <button onClick={handleClick}>Incrémenter</button> <h2>{count></h2> <Nom de l'enfant={"joe"} childFunc={handler} /> </div> ); }
Le code du composant enfant restera tel quel. Nous n'utiliserons pas les fonctions transmises depuis les composants parents dans les composants enfants :
//Child.js fonction d'exportation Enfant (accessoires) { console.log("Rendu enfant"); retour ( <div> <h2>{props.name></h2> </div> ); } export default React.memo(Child);
Désormais, lorsque nous incrémentons count
dans le composant parent, il restitue et restitue le composant enfant en même temps, même s'il n'y a aucun changement dans les paramètres transmis.
Alors, qu’est-ce qui provoque le nouveau rendu du sous-composant ? La réponse est que chaque fois que le composant parent effectue un nouveau rendu, une nouvelle fonction handler
est créée et transmise au composant enfant. Désormais, étant donné que la fonction handle
est recréée à chaque nouveau rendu, le composant enfant constatera que handler
a changé lors d'une comparaison superficielle des accessoires et restituera le composant enfant.
Ensuite, nous verrons comment résoudre ce problème.
useCallback()
pour éviter d'autres rendusLe principal problème avec le re-rendu du composant enfant est que la fonction handler
est recréée, ce qui modifie la référence transmise au composant enfant. Il nous faut donc trouver un moyen d’éviter cette duplication. Si la fonction handler
n'est pas recréée, la référence à la fonction handler
ne change pas, donc le composant enfant n'est pas restitué.
Pour éviter d'avoir à recréer la fonction à chaque fois que le composant parent est rendu, nous utiliserons un React Hook appelé useCallback(). Les hooks ont été introduits dans React 16. Pour en savoir plus sur les Hooks, vous pouvez consulter la documentation officielle des hooks de React, ou consulter « React Hooks : Comment démarrer et créer le vôtre ».
Le hook useCallback()
prend en compte deux paramètres : une fonction de rappel et une liste de dépendances. .
Ce qui suit est un exemple de useCallback()
:
const handleClick = useCallback(() => {. //Faire quelque chose }, [x,y]);
Ici, useCallback()
est ajouté à la fonction handleClick()
. Le deuxième paramètre [x, y]
peut être un tableau vide, une seule dépendance ou une liste de dépendances. La fonction handleClick()
n'est recréée que chaque fois que l'une des dépendances mentionnées dans le deuxième paramètre change.
Si les dépendances mentionnées dans useCallback()
n'ont pas changé, alors la version mémorisée de la fonction de rappel mentionnée comme premier argument est renvoyée. Nous allons modifier le composant parent pour utiliser useCallback()
sur les gestionnaires passés au composant enfant :
//Parent.js exporter la fonction par défaut Parent() { const [compte, setCount] = useState(0); const handleClick = () => { setCount(compte + 1); } ; const handler = useCallback(() => { // Utilisez useCallback() pour la fonction de gestionnaire console.log("gestionnaire"); }, []); console.log("Rendu parent"); retour ( <div className="Application"> <button onClick={handleClick}>Incrémenter</button> <h2>{count></h2> <Nom de l'enfant={"joe"} childFunc={handler} /> </div> ); }
Le code du composant enfant restera tel quel.
Le code complet de cet exemple se trouve dans ce bac à sable.
Lorsque nous incrémentons count
dans le composant parent du code ci-dessus, nous pouvons voir le résultat suivant :
Rendu parent Rendu enfant Rendu parent Rendu parent Rendu parent
Étant donné que nous utilisons useCallback()
pour handler
dans le composant parent, la fonction handler
ne sera pas recréée à chaque fois que le composant parent est restitué et la version Memoization handler
sera transmise au composant enfant. Le composant enfant effectuera une comparaison superficielle et remarquera que la référence à la fonction handler
n'a pas changé, il n'appellera donc pas la méthode render
.
la mémorisation est un bon moyen d'éviter un nouveau rendu inutile des composants lorsque leur état ou leurs accessoires n'ont pas changé, améliorant ainsi les performances des applications React. Vous pourriez envisager d'ajouter Memoization à tous vos composants, mais ce n'est pas nécessairement le moyen de créer des composants React hautes performances. La mémorisation ne doit être utilisée que si le composant :
Dans ce tutoriel, nous avons compris :
React.memo()
pour les composants de fonction et React.PureComponent
pour les composants de classe.React.memo()
useCallback()
éviter les problèmes de rendu lorsque les fonctions sont transmises en tant qu'accessoires aux composants enfants.J'espère que cette introduction à React Memoization vous sera utile !
Adresse originale : https://www.sitepoint.com/implement-memoization-in-react-to-improve-performance/Auteur original
: Nida Khan