Maison > interface Web > js tutoriel > Optimiser les performances de la réaction avec les composants apatrides

Optimiser les performances de la réaction avec les composants apatrides

Lisa Kudrow
Libérer: 2025-02-16 11:35:08
original
266 Les gens l'ont consulté

Optimizing React Performance with Stateless Components

Optimizing React Performance with Stateless Components

Cet article explore les composants apatrides dans React. Ce type de composant ne contient pas d'appels this.state = { ... } et ne traite que des composants «accessoires» entrants et enfants.

Résumé des points clés

  • Le composant sans état de React ne contient aucun appel this.state = { … }. Ils ne gèrent que les «accessoires» et sous-composants entrants, ce qui les rend plus simples et plus faciles à analyser du point de vue du test.
  • Les composants apatrides peuvent être convertis en composants fonctionnels, ce qui ressemble plus à un JavaScript pur et a moins de dépendance au cadre utilisé.
  • Les performances des composants sans état peuvent être optimisées en utilisant React.PureComponent, qui a une méthode shouldComponentUpdate intégrée qui permet des comparaisons peu profondes de chaque accessoire.
  • Recompose est une bibliothèque d'utilité pour les composants de fonction et les composants avancés dans React. Il peut être utilisé pour rendre les composants de la fonction sans rediffusion lorsque les accessoires sont inchangés.
  • Les composants apatrides peuvent améliorer considérablement les performances des applications React car elles ne gèrent pas leurs propres méthodes d'état ou de cycle de vie, réduisant ainsi le volume de code et la consommation de mémoire. Cependant, ils ont également certaines limites, comme ne pas être en mesure d'utiliser des méthodes de cycle de vie ou de gérer leur propre état.

bases

import React, { Component } from 'react'

class User extends Component {
  render() {
    const { name, highlighted, userSelected } = this.props
    console.log('Hey User is being rendered for', [name, highlighted])
    return <div>
      <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}}
         onClick={event => {
           userSelected()
         }}>
        {name}
      </h3>
    </div>
  }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

Le code s'exécute normalement. C'est très basique, mais il construit des exemples.

Il faut noter:

  • Il est apatride, sans this.state = { ... }.
  • console.log est utilisé pour comprendre son utilisation. Surtout lorsque vous effectuez une optimisation des performances, si les accessoires ne sont pas réellement modifiés, le réinstallation inutile doit être évité.
  • Le gestionnaire d'événements ici est "en ligne". Cette syntaxe est pratique car son code est proche des éléments qu'il traite, et cette syntaxe signifie que vous n'avez rien à faire .bind(this).
  • L'utilisation d'une telle fonction en ligne aura une petite perte de performances, car la fonction doit être créée à chaque fois qu'il est rendu. Ce sera détaillé plus tard.

Afficher le composant

Nous réalisons maintenant que le composant ci-dessus n'est pas seulement apatride, c'est en fait ce que Dan Abramov appelle un composant d'affichage. C'est juste un nom, mais fondamentalement, il est léger, produit du HTML / DOM et ne gère aucune données d'état.

afin que nous puissions en faire une fonction! Non seulement cela semble "cool", mais il le rend également moins terrible car il est plus facile à comprendre. Il reçoit des entrées et, indépendamment de l'environnement, renvoie toujours la même sortie. Bien sûr, il "rappelle" parce que l'un des accessoires est une fonction appelable.

Réécrivons-le:

import React, { Component } from 'react'

class User extends Component {
  render() {
    const { name, highlighted, userSelected } = this.props
    console.log('Hey User is being rendered for', [name, highlighted])
    return <div>
      <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}}
         onClick={event => {
           userSelected()
         }}>
        {name}
      </h3>
    </div>
  }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

ça fait du bien, non? Cela ressemble à de pur javascript, vous pouvez l'écrire sans penser au cadre que vous utilisez.

Le problème de la rediffusion continue

Supposons que notre composant utilisateur est utilisé dans un composant qui change l'état au fil du temps. Mais l'État n'affecte pas nos composants. Par exemple, comme ceci:

const User = ({ name, highlighted, userSelected }) => {
  console.log('Hey User is being rendered for', [name, highlighted])
  return <div>
    <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}}
       onClick={event => {
         userSelected()
       }}>
      {name}
    </h3>
  </div>
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

Exécutez ce code et vous remarquerez que même si rien ne change, nos composants seront renvoyés! Ce n'est pas un gros problème maintenant, mais dans les applications pratiques, les composants ont tendance à devenir de plus en plus complexes, et chaque rediffusion inutile entraînera le ralentissement du site Web.

Si vous utilisez react-addons-perf pour déboguer cette application maintenant, je suis sûr que vous constaterez que le temps est perdu sur le rendu Users->User. Oups! ce qu'il faut faire? !

Il semble que tout montre que nous devons utiliser shouldComponentUpdate pour couvrir la façon dont React pense que les accessoires sont différents, et nous sommes sûrs qu'ils ne sont pas différents. Afin d'ajouter un crochet de cycle de vie React, le composant doit être une classe. Hélas . Nous retournons donc à l'implémentation basée sur les classes originales et ajoutons une nouvelle méthode de crochet de lifting:

Retour vers le composant de classe

import React, { Component } from 'react'

class Users extends Component {
  constructor(props) {
    super(props)
    this.state = {
      otherData: null,
      users: [{name: 'John Doe', highlighted: false}]
    }
  }

  async componentDidMount() {
    try {
      let response = await fetch('https://api.github.com')
      let data = await response.json()
      this.setState({otherData: data})
    } catch(err) {
      throw err
    }
  }

  toggleUserHighlight(user) {
    this.setState(prevState => ({
      users: prevState.users.map(u => {
        if (u.name === user.name) {
          u.highlighted = !u.highlighted
        }
        return u
      })
    }))
  }

  render() {
    return <div>
      <h1>Users</h1>
      {
        this.state.users.map(user => {
          return <User
            name={user.name}
            highlighted={user.highlighted}
            userSelected={() => {
              this.toggleUserHighlight(user)
            }}/>
         })
      }
    </div>
  }
}
Copier après la connexion

Faites attention à la méthode nouvellement ajoutée shouldComponentUpdate. C'est un peu moche. Non seulement nous ne pouvons plus utiliser de fonctions, mais nous devons également énumérer manuellement les accessoires qui peuvent être modifiés. Cela implique une hypothèse audacieuse que l'hélice de fonction userSelected ne changera pas. Cela est peu probable, mais cela a besoin d'attention.

mais veuillez noter que même si le composant d'application inclus est renvoyé, cela ne sera rendu qu'une seule fois! Donc, c'est bon pour les performances. Mais pouvons-nous faire mieux?

react.pureComponent

Depuis React 15.3, il existe une nouvelle classe de base de composants. Il s'appelle PureComponent, et il a une méthode shouldComponentUpdate intégrée qui fait une "comparaison peu profonde" de chaque accessoire. merveilleux! Si nous l'utilisons, nous pouvons supprimer notre méthode personnalisée shouldComponentUpdate qui doit répertorier des accessoires spécifiques.

import React, { Component } from 'react'

class User extends Component {
  render() {
    const { name, highlighted, userSelected } = this.props
    console.log('Hey User is being rendered for', [name, highlighted])
    return <div>
      <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}}
         onClick={event => {
           userSelected()
         }}>
        {name}
      </h3>
    </div>
  }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

Essayez-le et vous serez déçu. Il se redevient à chaque fois. Pourquoi? ! La réponse est due au fait que la fonction userSelected est recréée à chaque fois dans la méthode render de l'application. Cela signifie que lorsque le composant basé sur un PureComponent appelle son propre shouldComponentUpdate(), il renvoie true, car la fonction est toujours différente, car elle est créée à chaque fois.

Habituellement, la solution à ce problème est de lier la fonction du constructeur qui contient le composant. Premièrement, si nous faisons cela, cela signifie que nous devons taper le nom de la méthode 5 fois (et 1 avant):

  • this.userSelected = this.userSelected.bind(this) (dans le constructeur)
  • userSelected() { ... } (en tant que définition de la méthode elle-même)
  • <User ... userSelected={this.userSelected} ... /> (où le rendu du composant utilisateur est défini)

Un autre problème est que, comme vous pouvez le voir, lorsque la méthode userSelected est réellement exécutée, cela dépend d'une fermeture. En particulier, il s'appuie sur la variable de portée this.state.users.map() de l'itérateur. user

Certes, il existe une solution à ce problème, qui est d'abord de lier la méthode

à userSelected puis lorsque la méthode est appelée (dans un composant enfant), passez le this (ou son nom ) revenez en arrière. Voici une solution comme celle-ci. user

Recomposer pour sauver!

Tout d'abord, passons en revue nos objectifs:

  1. L'écriture des composants fonctionnels se sent mieux car ce sont des fonctions. Cela indique immédiatement au lecteur de code qu'il n'enregistre aucun état. Ils sont faciles à raisonner du point de vue des tests unitaires. Et ils se sentent plus concis et plus comme un JavaScript pur (et bien sûr JSX).
  2. Nous sommes trop paresseux pour lier toutes les méthodes transmises aux composants enfants. Bien sûr, si les méthodes sont complexes, il est préférable de les refactorricant au lieu de les créer dynamiquement. Les méthodes de création dynamique signifient que nous pouvons écrire leur code directement là où ils sont utilisés, nous n'avons pas à les nommer, et nous les avons mentionnés 5 fois dans trois endroits différents.
  3. Les sous-composants ne doivent être réintégrés que lorsque les accessoires changent. Cela peut ne pas avoir d'importance pour les petits et rapides, mais pour les applications pratiques, lorsque vous avez beaucoup de ces composants, tous ces rendus supplémentaires gaspillent le processeur lorsque peut être évité.
(En fait, notre situation idéale est que le composant n'est rendu qu'une seule fois. Pourquoi ne peut pas réagir à résoudre ce problème pour nous? Dans ce cas, les articles de blog "Comment réagir plus rapidement" seront réduits de 90% .)

Selon la documentation, Recompose est "une bibliothèque d'outils utilitaires React pour les composants de la fonction et les composants avancés. Considérez-le comme un Lodash pour React."

. Il y a beaucoup à explorer dans cette bibliothèque, mais maintenant nous voulons rendre nos composants de fonction sans rediffusion lorsque les accessoires n'ont pas changé. La première fois que nous avons essayé de le réécrire dans le composant de fonction avec Recompose.pure, il ressemble à ceci:

import React, { Component } from 'react'

class User extends Component {
  render() {
    const { name, highlighted, userSelected } = this.props
    console.log('Hey User is being rendered for', [name, highlighted])
    return <div>
      <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}}
         onClick={event => {
           userSelected()
         }}>
        {name}
      </h3>
    </div>
  }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

Vous remarquerez peut-être que si vous exécutez ce code, le composant utilisateur se renvoie toujours même si les clés des accessoires (

et

) n'ont pas changé. name highlighted allons plus loin. Nous n'utilisons pas

, mais nous utilisons

, qui est une version de recompose.pure, mais vous pouvez spécifier la clé de l'hélice sur laquelle vous souhaitez vous concentrer: recompose.onlyUpdateForKeys recompose.pure

const User = ({ name, highlighted, userSelected }) => {
  console.log('Hey User is being rendered for', [name, highlighted])
  return <div>
    <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}}
       onClick={event => {
         userSelected()
       }}>
      {name}
    </h3>
  </div>
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

Après avoir exécuté ce code, vous remarquerez qu'il ne sera mis à jour que lorsque

ou

les accessoires changent. Si le composant parent est renvoyé, le composant utilisateur ne le fait pas. name highlighted Vive! Nous avons trouvé de l'or!

Discussion

Tout d'abord, demandez-vous si cela vaut la peine d'optimiser les performances de votre composant. Peut-être que c'est plus que ça en vaut la peine. Vos composants doivent être légers, vous pouvez peut-être déplacer tous les calculs coûteux des composants et les déplacer dans des fonctions mémorables externes, ou vous pouvez réorganiser vos composants afin que certaines données ne puissent pas être un gaspillage de composants de rendu lorsqu'ils sont utilisés. Par exemple, dans cet exemple, vous ne voudrez peut-être pas rendre le composant utilisateur avant que Fetch ne soit terminé.

Écrire le code de la manière la plus pratique pour vous, puis démarrer votre programme et itérer à partir de là pour le rendre meilleur n'est pas une mauvaise solution. Dans cet exemple, afin d'améliorer les performances, vous devez définir le composant de fonction à partir de:

import React, { Component } from 'react'

class User extends Component {
  render() {
    const { name, highlighted, userSelected } = this.props
    console.log('Hey User is being rendered for', [name, highlighted])
    return <div>
      <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}}
         onClick={event => {
           userSelected()
         }}>
        {name}
      </h3>
    </div>
  }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

… changé en…

const User = ({ name, highlighted, userSelected }) => {
  console.log('Hey User is being rendered for', [name, highlighted])
  return <div>
    <h3 style={{fontStyle: highlighted ? 'italic' : 'normal'}}
       onClick={event => {
         userSelected()
       }}>
      {name}
    </h3>
  </div>
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

Idéalement, plutôt que de montrer un moyen de contourner le problème, la meilleure solution est un nouveau patch de React, ce qui fait une énorme amélioration à shallowEqual à l'identification "automatiquement" de l'entrée et de la comparaison est une fonction, et simplement parce que Cela n'est pas égal ne signifie pas qu'il est réellement différent.

d'accord! Il existe une alternative de compromis à devoir faire face aux fonctions en ligne qui lient les méthodes dans le constructeur et chaque récréation. C'est le domaine de la classe publique. Il s'agit d'une fonctionnalité de phase 2 dans Babel, donc votre configuration le soutiendra probablement. Par exemple, voici une branche qui l'utilise, ce qui est non seulement plus court, mais maintenant, nous n'avons pas besoin de répertorier manuellement tous les accessoires hors fonction. Cette solution doit abandonner les fermetures. Néanmoins, il est toujours bon de savoir et de réaliser recompose.onlyUpdateForKeys en cas de besoin.

Pour plus d'informations sur React, veuillez consulter notre cours "React the ES6 Way".

Cet article a été évalué par des pairs par Jack Franklin. Merci à tous les pair-évaluateurs de SitePoint pour avoir rendu le contenu de SitePoint parfait!

Les questions fréquemment posées sur l'optimisation des performances de la réaction avec les composants sans état

Quelle est la principale différence entre un composant avec état et un composant apatride dans React?

Les composants d'état (également appelés composants de classe) maintiennent la mémoire des changements de l'état des composants au fil du temps. Ils sont responsables de la façon dont les composants se comportent et rendent. D'un autre côté, les composants apatrides (également appelés composants fonctionnels) n'ont pas leur propre état. Ils reçoivent des données du composant parent sous forme d'accessoires et les rendent. Ils sont principalement responsables de la partie UI de la composante.

Comment optimiser les performances de mon application React à l'aide de composants sans état?

Les composants apatrides peuvent considérablement améliorer les performances des applications React. Puisqu'ils ne gèrent pas leurs propres méthodes d'état ou de cycle de vie, ils ont moins de code, ce qui les rend plus faciles à comprendre et à tester. Ils peuvent également réduire la quantité de mémoire consommée par l'application. Pour optimiser les performances, vous pouvez convertir autant que possible les composants d'état en composants sans état et utiliser REACT PureComponent pour éviter une rediffusion inutile.

Qu'est-ce que PureComponent et comment aide-t-il à optimiser les performances de réaction?

PureComponent est un composant REACT spécial qui aide à optimiser les performances de votre application. Il utilise une comparaison peu profonde de l'hélice et de l'état pour implémenter la méthode du cycle de vie shouldComponentUpdate. Cela signifie qu'il ne renvoiera que lorsque l'État ou les accessoires changent, ce qui peut réduire considérablement le redémarrage inutile et améliorer les performances.

Comment convertir un composant d'état dans REACT en un composant apatride?

La conversion d'un composant d'état en un composant sans état comprend la suppression de toute méthode d'état ou de cycle de vie du composant. Le composant ne doit recevoir que des données par le biais d'accessoires et les rendre. Voici un exemple:

// Composant d'état La classe Welcome étend React. rendre() { return

Bonjour, {this.props.name}
; } }

// Composant sans état fonction bienvenue (accessoires) { return

Bonjour, {props.name}
; }

Quelles sont les meilleures pratiques pour utiliser des composants sans état dans React?

Certaines meilleures pratiques pour l'utilisation de composants sans état dans REACT: Utilisez autant que possible pour améliorer les performances et la lisibilité; Il est également recommandé d'utiliser des accessoires de déconstruction pour écrire du code de nettoyage.

Les composants apatrides peuvent-ils utiliser des méthodes de cycle de vie dans React?

Non, les composants apatrides ne peuvent pas utiliser les méthodes de cycle de vie dans React. Les méthodes de cycle de vie ne sont applicables qu'aux composants de classe. Cependant, avec l'introduction de crochets dans React 16.8, vous pouvez désormais ajouter des fonctionnalités d'état et de cycle de vie aux composants de fonction.

Quelles sont les limites des composants apatrides en réaction?

Bien que les composants apatrides présentent de nombreux avantages, ils ont également certaines limites. Ils ne peuvent pas utiliser les méthodes de cycle de vie ou gérer leur propre état. Cependant, ces limitations peuvent être surmontées en utilisant des crochets React.

Comment les composants apatrides peuvent-ils améliorer la lisibilité du code?

Les composants apatrides améliorent la lisibilité du code grâce à la simplicité et à la clarté. Ils ne gèrent pas leur propre état ou n'utilisent pas de méthodes de cycle de vie, ce qui les rend plus simples et plus faciles à comprendre. Ils encouragent également l'utilisation de petits composants réutilisables, ce qui peut rendre le code plus organisé et plus facile à entretenir.

Les composants apatrides peuvent-ils gérer les événements dans React?

Oui, les composants apatrides peuvent gérer les événements dans React. Les gestionnaires d'événements peuvent être adoptés sous forme d'accessoires aux composants apatrides. Voici un exemple:

fonction actionLink (accessoires) { Retour ( Cliquez sur moi )); }

Comment les composants sans état favorisent-ils la modularisation du code?

Les composants apatrides favorisent la modularisation du code en favorisant l'utilisation de petits composants réutilisables. Chaque composant peut être développé et testé indépendamment, ce qui rend le code plus facile à maintenir et à comprendre. Il encourage également la séparation des préoccupations, où chaque composant est responsable d'une seule fonction.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal