Guerre aux freins. Optimisation du nombre de rendus de composants dans React Native

Bonjour, Habr! Je m'appelle Kamo Spertsyan, je suis engagé dans le développement React Native chez Profi.ru. Si vous décidez d' utiliser la technologie React Native pour fournir rapidement les fonctionnalités du produit et vous concentrer sur la vitesse de développement, vous risquez de rencontrer des problèmes de performances. C'est du moins ce qui nous est arrivé. Après six mois de développement actif, les performances de notre application sont tombées en dessous d'un niveau critique - tout était extrêmement lent. Par conséquent, nous avons repris l'optimisation - supprimé tous les «freins» lors du démarrage, les transitions entre les écrans, le rendu des écrans, les réactions aux actions de l'utilisateur. En conséquence, en trois mois, ils ont amené l'expérience utilisateur au niveau natif. Dans cet article, je veux parler de la façon dont nous avons optimisé l'application sur React Native et résolu le problème des rendus de plusieurs composants.



J'ai rassemblé des recommandations qui aideront à minimiser le nombre de redessins inutiles de composants. Pour plus de clarté, dans les exemples, je compare les implémentations «mauvaises» et «bonnes». L'article sera utile à ceux qui sont déjà confrontés à de mauvaises performances des applications et à ceux qui ne veulent pas autoriser cela à l'avenir.

Nous utilisons React Native associé à Redux. Certains conseils sont liés à cette bibliothèque. Toujours dans l'exemple, j'utilise la bibliothèque Redux-thunk - pour simuler le travail avec le réseau.

Quand penser à la performance?


En fait, il convient de se rappeler dès le début des travaux sur l'application. Mais si votre application ralentit déjà - ne désespérez pas, tout peut être corrigé.

Tout le monde le sait, mais juste au cas où, je mentionnerai: il vaut mieux vérifier les performances sur les appareils faibles. Si vous développez sur des appareils puissants, vous ne connaissez peut-être pas les «freins» des utilisateurs finaux. Décidez par vous-même des appareils qui vous guideront. Mesurer le temps ou FPS dans les parcelles de contrôle pour comparer avec les résultats après optimisation.

React Native prêt à l'emploi offre la possibilité de mesurer les applications FPS via Developer Tools → Show perf monitor. La valeur de référence est de 60 images par seconde. Plus cet indicateur est bas, plus l'application "ralentit" - ne répond pas ou réagit avec retard aux actions de l'utilisateur. L'un des principaux effets sur le FPS est le nombre de rendus, dont la «sévérité» dépend de la complexité des composants.

Exemple de description


Je montre toutes les recommandations sur l'exemple d'une application simple avec une liste de nouveautés. L'application dispose d'un écran, qui est situé FlatListavec les nouvelles. Une news est un composant NewsItemqui se compose de deux composants plus petits - le titre ( NewsItemTitle) et le corps ( NewsItemBody). L'exemple complet peut être vu ici . Plus loin dans le texte se trouvent des liens vers diverses branches du référentiel pour des exemples spécifiques. Le référentiel est utilisé pour la commodité des lecteurs qui souhaitent explorer des exemples plus en profondeur. Le code dans le référentiel et les exemples ci-dessous ne prétendent pas être parfaits - il est uniquement nécessaire à des fins de démonstration.

Ci-dessous, tous les composants sont schématisés avec des liens et des accessoires.


Dans la méthode de rendu de chaque composant, j'ai ajouté à la console des informations uniques à ce sujet:

SCREEN
ITEM_{no}
ITEM_TITLE_{no}
ITEM_BODY_{no}

{no}est le numéro de série des nouvelles afin de faire la distinction entre différents rendus de nouvelles de plusieurs rendus du même.

Pour tester sur chaque refreshliste de nouvelles, des nouvelles supplémentaires sont ajoutées à son début. Dans le même temps, le message suivant s'affiche dans la console:

--------------[ REFRESHING ]--------------

Ces enregistrements aideront à comprendre s'il y a un problème dans un composant particulier et à déterminer ultérieurement s'il était possible de l'optimiser.

S'il est correctement implémenté, notre journal après le lancement et plusieurs mises à jour devraient ressembler à ceci:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
--------------[ REFRESHING ]--------------
SCREEN
ITEM_4
ITEM_TITLE_4
ITEM_BODY_4

Au premier démarrage, l'écran lui-même et deux premières informations sont dessinés. Lors de la mise à jour de la carte, l'écran est à nouveau rendu, car ses données ont vraiment changé. Plus de nouvelles arrivent. Toutes les nouvelles précédentes ne sont pas redessinées, car leurs données n'ont pas changé.

Quand un composant est-il rendu?


Dans React et React Native, il existe deux conditions pour rendre un composant:

  1. changer ses accessoires / état,
  2. rendu du composant parent.

Une fonction peut être redéfinie dans un composant shouldComponentUpdate- elle reçoit de nouveaux accessoires et états en entrée et indique si le composant doit être rendu. Souvent, pour éviter des rendus inutiles, une comparaison superficielle des accessoires et des objets d'état suffit. Par exemple, cela élimine les rendus inutiles lorsque le composant parent change, s'ils n'affectent pas le composant enfant. Afin de ne pas écrire une comparaison de surface manuellement à chaque fois, vous pouvez hériter d'un composant React.PureComponentqui encapsule cette vérification.

Lorsque nous utilisons la fonction de lien de connexion, la bibliothèque Redux crée un nouveau composant qui est «connecté» à l'état global. Les modifications de cet état déclenchent une méthodemapStateToPropsqui renvoie de nouveaux accessoires. Ensuite, une comparaison des anciens et des nouveaux accessoires commence, que le composant ait été déclaré PureComponentou non.

Considérez ces nuances dans notre exemple.

Nous allons NewsItemlaisser passer le composant connect, NewsItemTitlehériter de React.Component, et NewsItemBody- de React.PureComponent.

Exemple de code complet

export class NewsItemTitle extends React.Component
export class NewsItemBody extends React.PureComponent

Voici à quoi ressemblera le journal après une mise à jour d'une carte:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
ITEM_2
ITEM_TITLE_2
ITEM_1
ITEM_TITLE_1

Vous pouvez voir que les composants d'actualités et de titres sont redessinés. Nous les examinerons tour à tour.

NewsItemdéclaré en utilisant connect. En tant qu'accessoire, ce composant reçoit un identifiant, par lequel il reçoit ensuite des nouvelles dans mapStateToProps:

const mapStateToProps = (state, ownProps) => ({
  item: state.newsMap[ownProps.itemKey],
});

Étant donné que lors de la mise à jour de la carte, toutes les nouvelles sont téléchargées à nouveau, l'objet sera mis à itemjour et se référera ensuite à diverses cellules de mémoire. En d'autres termes, ce seront des objets différents, même si tous les champs contenus sont les mêmes. Par conséquent, une comparaison entre la composante précédente et la nouvelle composante State'ov renvoie false. Le composant sera restitué, malgré le fait que les données n'ont pas changé.

NewsItemTitleest hérité de React.Component, il est donc rendu à chaque fois que le composant parent est rendu. Cela se produit indépendamment des valeurs des anciens et des nouveaux accessoires.

NewsItemBodyhérité de React.PureComponent, il compare donc les anciens et les nouveaux accessoires. Dans les news 1 et 2, leurs valeurs sont équivalentes, donc le composant est rendu uniquement pour les news 3.

Pour optimiser les rendusNewsItemTitledéclarez-le simplement comme React.PureComponent. Dans le cas de, vous devez NewsItemredéfinir la fonction shouldComponentUpdate:

shouldComponentUpdate(nextProps) {
  return !shallowEqual(this.props.item, nextProps.item);
}

Exemple de code complet

Voici shallowEqualune fonctionnalité de comparaison de surface d'objets fournie par Redux. Vous pouvez écrire comme ceci:

shouldComponentUpdate(nextProps) {
  return (
    this.props.item.title !== nextProps.item.title ||
    this.props.item.body !== nextProps.item.body
  );
}

Voici à quoi ressemblera notre journal après ceci:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3

Remarque
shouldComponentUpdate NewsItem , NewsItemTitle . . NewsItemTitle - NewsItem, .

React.memo et composants fonctionnels


Il n'est shouldComponentUpdatepas possible de remplacer un composant fonctionnel. Mais cela ne signifie pas que pour optimiser un composant fonctionnel, vous devez le réécrire dans un composant de classe. Dans de tels cas, la fonction de mémorisation React.memo est fournie . Il accepte une entrée de composant et une fonction de comparaison facultative areEqual. Lorsqu'il est appelé, il areEqualobtient des accessoires anciens et nouveaux et devrait renvoyer le résultat de la comparaison. La différence avec shouldComponentUpdatece qui areEqualdevrait revenir truesi les accessoires sont égaux, et non l'inverse.

Par exemple, la NewsItemTitlemémorisation peut ressembler à ceci:

areEqual(prevProps, nextProps) {
  return shallowEqual(prevProps, nextProps);
}
export OptimizedNewsItemTitle = React.memo(NewsItemTitle, areEqual)

Si vous ne réussissez pas areEqualà React.memo, puis une comparaison superficielle des accessoires sera faite, de sorte que notre exemple peut être simplifiée:

export OptimizedNewsItemTitle = React.memo(NewsItemTitle)

Fonctions lambda dans les accessoires


Pour traiter les événements des composants, les fonctions peuvent être passées à ses accessoires. L'exemple le plus frappant est la mise en œuvre onPress. Souvent, des fonctions lambda anonymes sont utilisées pour cela. Disons que NewsItemBodynous voulons afficher uniquement l'aperçu, et si vous cliquez dessus - tout le texte. Pour ce faire, lors du rendu NewsItem, NewsItemBodynous passerons l'hélice suivante:

<NewsItemBody
  ...
  onPress={() => this.props.expandBody()}
  ...
/>

Voici à quoi ressemble le journal avec cette implémentation lorsque la méthode shouldComponentUpdateest NewsItemsupprimée:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1

Les organes de presse 1 et 2 sont rendus, bien que leurs données n'aient pas changé, mais le NewsItemBodysont PureComponent. Cela est dû au fait que pour chaque rendu, la NewsItemvaleur des accessoires onPressest recréée. Techniquement, onPressà chaque rendu, il pointe vers une nouvelle zone en mémoire, donc une comparaison superficielle des accessoires en NewsItemBodyretourne faux. Le problème est résolu par l'entrée suivante:

<NewsItemBody
  ...
  onPress={this.props.expandBody}
  ...
/>

Journal:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
ITEM_2
ITEM_TITLE_2
ITEM_1
ITEM_TITLE_1

Exemple de code complet

Malheureusement, une fonction anonyme ne peut en aucun cas toujours être réécrite en tant que méthode ou champ de classe pour un tel enregistrement. Le cas le plus courant est lorsque, à l'intérieur de la fonction lambda, les variables de portée de la fonction dans laquelle elle est déclarée sont utilisées.

Considérez ce cas dans notre exemple. Pour passer de la liste générale à l'écran d'une actualité, nous ajoutons le traitement du clic sur le corps de l'actualité. La méthode des renderItemcomposants FlatListressemblera à ceci:

const renderItem = ({item}) => (
  <NewsItem
    itemKey={item}
    onBodyPress={() => this.onItemBodyPress(item)}
  />
);

Une fonction anonyme ne onBodyPresspeut pas être déclarée dans une classe, car alors la variable itemnécessaire pour accéder à une actualité spécifique disparaîtra de la portée .

La solution la plus simple au problème consiste à modifier la signature des accessoires du onBodyPresscomposant NewsItemafin que le paramètre requis soit transmis à la fonction lors de son appel. Dans ce cas, il s'agit de l'identifiant de news.

const renderItem = ({item}) => (
  <NewsItem
    itemKey={item}
    onBodyPress={item => this.onItemBodyPress(item)}
  />
);

Dans ce cas, nous pouvons déjà supprimer la fonction anonyme dans la méthode de classe de composant.

const renderItem = ({item}) => (
  <NewsItem
    itemKey={item}
    onBodyPress={this.onItemBodyPress}
  />
);

Cependant, une telle solution nous obligera à changer le composant NewsItem.

class NewsItemComponent extends React.Component {
render() {
  ...
  return (
      ...
      <NewsItemBody
        ...
        onPress={() => this.props.onBodyPress(this.props.item)}
        ...
      />
      ...
  );
}

Et encore une fois, nous revenons au problème indiqué - nous passons une nouvelle fonction lambda au composant enfant pour chaque rendu du parent. Ce n'est que maintenant que nous avons baissé d'un niveau. Journal:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1

Pour vous débarrasser de ce problème à la racine, vous pouvez utiliser le hook useCallback . Il permet de mémoriser un appel de fonction en passant un argument. Si l'argument de la fonction ne change pas, le résultat de l'appel useCallbackpointera vers la même zone de mémoire. Dans notre exemple, cela signifie que lorsque vous redessinez les mêmes nouvelles, le onPresscomposant prop NewsItemBodyne change pas. Les crochets ne peuvent être utilisés que dans les composants fonctionnels, donc l'aspect final du composant NewsItemsera le suivant:

function NewsItemComponent(props) {
  ...
  const {itemKey, onBodyPress} = props.item;
  const onPressBody = useCallback(() => onBodyPress(itemKey), [itemKey, onBodyPress]);
  return (
    <View>
      ...
      <NewsItemBody
        ...
        onPress={onPressBody}
        ...
      />
    </View>
  );
}

Et le journal:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
ITEM_2
ITEM_TITLE_2
ITEM_1
ITEM_TITLE_1

Exemple de code complet

Tableaux et objets


En JavaScript, les fonctions sont représentées comme des objets, avec des tableaux. Par conséquent, l'exemple du bloc précédent est un cas particulier de création d'un nouvel objet dans les accessoires. C'est assez courant, alors je l'ai mis dans un paragraphe séparé.

Toute création de nouvelles fonctions, tableaux ou objets dans les accessoires conduit à un nouveau rendu des composants. Considérez cette règle dans l'exemple suivant. Passons dans un NewsItemBodystyle combiné de deux valeurs:

<NewsItemBody
  ...
  style={[styles.body, styles.item]}
  ...
/>

Et encore une fois, le journal montre les rendus de composants supplémentaires:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1

Pour résoudre ce problème, vous pouvez sélectionner un style distinct qui se combinera bodyet item, ou, par exemple, déplacera la déclaration du tableau [styles.body, styles.item]dans une variable globale.

Exemple de code complet

Réducteurs de réseau


Prenons une autre source populaire de «freins» associée à l'utilisation FlatList. Une application classique qui contient une longue liste d'éléments du serveur implémente la pagination. Autrement dit, il charge un ensemble limité d'éléments sous la forme de la première page, lorsque la liste des éléments actuels se termine, il charge la page suivante, etc. Un réducteur de liste d'articles pourrait ressembler à ceci:

const newsIdList = (state = [], action) => {
  if (action.type === 'GOT_NEWS') {
    return action.news.map(item => item.key);
  } else if (action.type === 'GOT_OLDER_NEWS') {
    return [...state, ...action.news.map(item => item.key)];
  }
  return state;
};

Lorsque chaque page suivante se charge dans le style de l'application, un nouveau tableau d'identificateurs est créé. Si nous transmettons ce tableau aux accessoires plus tard FlatList, voici à quoi ressembleront les journaux de rendu des composants:

SCREEN
ITEM_<1..10>
--------------[ LOADING NEXT PAGE ]--------------
SCREEN
ITEM_<1..10>
ITEM_<1..20>
--------------[ LOADING NEXT PAGE ]--------------
SCREEN
ITEM_<1..20>
ITEM_<1..30>

Pour cet exemple, j'ai apporté quelques modifications dans l'application de test.

  • Définissez la taille de la page sur 10 actualités.
  • item NewsItem FlatList-, connect. NewsItem React.Component .
  • .
  • . №1 .

L'exemple montre que lors du chargement de chaque page suivante, tous les anciens éléments sont à nouveau rendus, puis les anciens éléments et les éléments de la nouvelle page sont à nouveau rendus. Pour les amateurs de mathématiques: si la taille de la page est égale X, alors lorsque la iième page est chargée, au lieu de ne rendre que les Xnouveaux éléments, les éléments sont rendus (i - 1) * X + i * X.

«D'accord», dites-vous, «je comprends pourquoi tous les éléments sont dessinés après avoir ajouté une nouvelle page: le réducteur a renvoyé un nouveau tableau, une nouvelle zone de mémoire, tout ça. Mais pourquoi devons-nous rendre l'ancienne liste avant d'ajouter de nouveaux éléments? » «Bonne question», je vais vous répondre. Ceci est une conséquence du travail avec l'état du composant VirtualizedListsur la base duquelFlatList. Je n'entrerai pas dans les détails, car ils tirent sur un article séparé. Peu importe, je vous conseille de vous plonger dans la documentation et la source.

Comment se débarrasser d'une telle non-optimalité? Nous réécrivons le réducteur pour qu'il ne retourne pas un nouveau tableau pour chaque page, mais ajoute des éléments à celui existant:

Attention! Antipattern!
. , , , PureComponent, . , . . Redux.

const newsIdList = (state = [], action) => {
  if (action.type === 'GOT_NEWS') {
    return action.news.map(item => item.key);
  } else if (action.type === 'GOT_OLDER_NEWS') {
    action.news.forEach(item => state.push(item.key));
    return state;
    // return [...state, ...action.news.map(item => item.key)];
  }
  return state;
};

Après cela, notre journal ressemblera à ceci:

SCREEN
ITEM_<1..10>
--------------[ LOADING NEXT PAGE ]--------------
SCREEN
ITEM_<1..20>
--------------[ LOADING NEXT PAGE ]--------------
SCREEN
ITEM_<1..30>

Nous nous sommes débarrassés du rendu des anciens éléments avant d' ajouter des éléments à une nouvelle page, mais les anciens éléments sont toujours dessinés après la mise à jour de la liste. Le nombre de rendus pour la page suivante est désormais égal i * X. La formule est devenue plus simple, mais nous ne nous arrêterons pas là. Nous n'avons que de Xnouveaux éléments et nous voulons seulement de Xnouveaux rendus. Nous utiliserons les astuces déjà connues pour supprimer les rendus d'actualités qui n'ont pas changé d'accessoires. Connectez-vous à NewsItem:

SCREEN
ITEM_<1..10>
--------------[ LOADING NEXT PAGE ]--------------
SCREEN
ITEM_<11..20>
--------------[ LOADING NEXT PAGE ]--------------
SCREEN
ITEM_<21..30>

Bien! Maintenant, nous pouvons être satisfaits de nous-mêmes. Il n'y a nulle part où optimiser.

Exemple de code complet

Un lecteur attentif indiquera qu'après avoir appliqué la connexion au NewsItemjournal, il ressemblera au dernier exemple, quelle que soit la manière dont vous implémentez le réducteur. Et il aura raison - si le composant news vérifie ses accessoires avant le rendu, peu importe si l'ancien tableau est utilisé par le réducteur ou s'il en crée un nouveau. Seuls de nouveaux éléments sont dessinés et une seule fois. Cependant, changer l'ancien tableau au lieu d'en créer un nouveau nous évite des rendus inutiles du composant FlatListutilisé VirtualizedListet des itérations inutiles des vérifications d'équivalence des accessoires NewsItem. Avec un grand nombre d'éléments, cela donne également une augmentation des performances.

Utiliser des tableaux mutables et des objets dans les réducteurs doit être extrêmement prudent. Dans cet exemple, cela est justifié, mais si vous avez, disons, normal PureComponent, alors lorsque vous ajoutez des éléments au tableau mutable, les composants ne seront pas rendus. Ses accessoires restent en fait inchangés, car avant et après la mise à jour du tableau pointe vers la même zone mémoire. Cela peut entraîner des conséquences inattendues. Pas étonnant que l'exemple décrit viole les principes de Redux .

Et quelque chose d'autre...


Si vous utilisez des bibliothèques de niveau présentation, je vous conseille de vous assurer de bien comprendre comment elles sont implémentées. Dans notre application, nous utilisons un composant Swipeablede la bibliothèque react-native-gesture-handler. Il vous permet d'implémenter un bloc d'actions supplémentaires lors du glissement d'une carte de la liste.

En code, cela ressemble à ceci:

<Swipeable
  ...
  renderRightActions={this.renderRightActions}
  ...
>

Méthode renderRightActionsou renderLeftActionsretourne le composant qui s'affiche après le balayage. Nous avons déterminé et modifié la hauteur du panneau lors du changement de composants afin de l'adapter au contenu nécessaire. Il s'agit d'un processus gourmand en ressources, mais s'il se produit pendant l'animation de balayage, l'utilisateur ne voit aucune interférence.


Le problème est que le composant Swipeableappelle la méthode renderRightActionsau moment du rendu du composant principal. Tous les calculs et même le rendu de la barre d'action, qui n'est pas visible avant le balayage, se produisent à l'avance. Ainsi, toutes ces actions sont effectuées pour toutes les cartes de la liste en même temps. Cela a provoqué des «freins» importants lors du défilement de la planche.

Le problème a été résolu de la manière suivante. Si le panneau d'actions est dessiné avec le composant principal, et non à la suite du balayage, la méthode renderRightActionsrenvoie un vide de la Viewtaille du composant principal. Sinon, nous dessinons le panel d'actions supplémentaires comme précédemment.
Je donne cet exemple parce que les bibliothèques de support ne fonctionnent pas toujours comme prévu. Et s'il s'agit de bibliothèques au niveau de la présentation, il est préférable de s'assurer qu'elles ne gaspillent pas de ressources inutiles.

résultats


Après avoir éliminé les problèmes décrits dans l'article, nous avons considérablement accéléré l'application sur React Native. Il est maintenant difficile de le distinguer en termes de performances d'un modèle similaire, implémenté nativement. Les rendus excessifs ont ralenti à la fois le chargement des écrans individuels et la réaction aux actions des utilisateurs. Surtout, il était visible sur les listes, où des dizaines de composants sont dessinés à la fois. Nous n'avons pas tout optimisé, mais les écrans principaux de l'application ne ralentissent plus.

Les principaux points de l'article sont brièvement énumérés ci-dessous.

  1. React Native : Props/State- .
  2. , React.PureComponent, , .
  3. , shouldComponentUpdate React.Memo .
  4. - . , (shallow compare). , .
  5. La prise en charge des bibliothèques de niveau présentation peut entraîner un gaspillage inattendu de ressources. Il convient d'être prudent dans leur application.

C'est tout. J'espère que vous trouverez ces informations utiles. Je serai heureux de toute rétroaction!

Sources utiles


  1. Comprendre le rendu dans React + Redux
  2. Comparaison d'objets en JavaScript
  3. Amélioration des performances des composants fonctionnels React à l'aide de React.memo ()
  4. Comment Discord atteint les performances iOS natives avec React Native

All Articles