Methoden zur Verbesserung und Optimierung der Leistung in React-Apps

Veröffentlicht: 2022-03-10
Kurze Zusammenfassung ↬ Seit der Einführung von React hat es die Art und Weise verändert, wie Front-End-Entwickler Webanwendungen erstellen, und sein virtuelles DOM ist berühmt für das effektive Rendern von Komponenten. In diesem Tutorial werden wir verschiedene Methoden zur Leistungsoptimierung in React-Anwendungen sowie die Funktionen von React besprechen, mit denen wir die Leistung verbessern können.

React ermöglicht es Webanwendungen, ihre Benutzeroberflächen (UIs) schnell zu aktualisieren, aber das bedeutet nicht, dass Ihre mittlere oder große React-Anwendung effizient funktioniert. Seine Leistung hängt davon ab, wie Sie React beim Erstellen verwenden, und von Ihrem Verständnis der Funktionsweise von React und des Prozesses, durch den Komponenten die verschiedenen Phasen ihres Lebenszyklus durchlaufen. React bietet viele Leistungsverbesserungen für eine Web-App, und Sie können diese Verbesserungen durch verschiedene Techniken, Funktionen und Tools erreichen.

In diesem Tutorial werden wir verschiedene Methoden zur Leistungsoptimierung in React-Anwendungen sowie die Funktionen von React besprechen, mit denen wir die Leistung verbessern können.

Wo fange ich an, die Leistung in einer React-Anwendung zu optimieren?

Wir können nicht mit der Optimierung einer App beginnen, ohne genau zu wissen, wann und wo optimiert werden soll. Sie fragen sich vielleicht: „Wo fangen wir an?“

Während des anfänglichen Rendering-Prozesses erstellt React einen DOM-Baum von Komponenten. Wenn sich also Daten im DOM-Baum ändern, möchten wir, dass React nur die Komponenten neu rendert, die von der Änderung betroffen waren, und die anderen Komponenten im Baum überspringt, die nicht betroffen waren.

React könnte jedoch dazu führen, dass alle Komponenten im DOM-Baum neu gerendert werden, obwohl nicht alle davon betroffen sind. Dies führt zu längeren Ladezeiten, verschwendeter Zeit und sogar verschwendeten CPU-Ressourcen. Das müssen wir verhindern. Darauf werden wir unsere Optimierungsbemühungen konzentrieren.

In dieser Situation könnten wir jede Komponente so konfigurieren, dass sie nur bei Bedarf rendert oder diffundiert, um Ressourcen- und Zeitverschwendung zu vermeiden.

Mehr nach dem Sprung! Lesen Sie unten weiter ↓

Leistungsmessung

Starten Sie den Optimierungsprozess Ihrer React-Anwendung niemals basierend auf dem, was Sie fühlen. Verwenden Sie stattdessen die verfügbaren Messwerkzeuge, um die Leistung Ihrer React-App zu analysieren und einen detaillierten Bericht darüber zu erhalten, was sie möglicherweise verlangsamt.

Analysieren von Reaktionskomponenten mit der Registerkarte "Leistung" von Chrome

Laut der Dokumentation von React können Sie, während Sie sich noch im Entwicklungsmodus befinden, die Registerkarte „Performance“ im Chrome-Browser verwenden, um zu visualisieren, wie React-Komponenten bereitgestellt, aktualisiert und deaktiviert werden. Das folgende Bild zeigt beispielsweise die Profilerstellung auf der Registerkarte „Leistung“ von Chrome und die Analyse meines Blogs im Entwicklungsmodus.

Zusammenfassung des Leistungsprofils
Leistungsprofiler-Zusammenfassung (große Vorschau)

Gehen Sie dazu folgendermaßen vor:

  1. Deaktivieren Sie vorübergehend alle Erweiterungen, insbesondere die React Developer Tools, da diese das Ergebnis der Analyse durcheinander bringen können. Sie können Erweiterungen einfach deaktivieren, indem Sie Ihren Browser im Inkognito-Modus ausführen.
  2. Stellen Sie sicher, dass die Anwendung im Entwicklungsmodus ausgeführt wird. Das heißt, die Anwendung sollte auf Ihrem Localhost ausgeführt werden.
  3. Öffnen Sie die Entwicklertools von Chrome, klicken Sie auf die Registerkarte „Leistung“ und dann auf die Schaltfläche „Aufzeichnen“.
  4. Führen Sie die Aktionen aus, die Sie profilieren möchten. Nehmen Sie nicht länger als 20 Sekunden auf, sonst hängt sich Chrome möglicherweise auf.
  5. Stoppen Sie die Aufnahme.
  6. Reaktionsereignisse werden unter dem Label „Benutzer-Timing“ gruppiert.

Die Zahlen vom Profiler sind relativ. Die meisten Zeiten und Komponenten werden in der Produktion schneller gerendert. Dies sollte Ihnen jedoch dabei helfen, herauszufinden, wann die Benutzeroberfläche versehentlich aktualisiert wird und wie tief und wie oft die Aktualisierungen der Benutzeroberfläche erfolgen.

Reagieren Sie auf den Entwicklertools-Profiler

Laut der Dokumentation von React sind in React react-dom 16.5+ und React react-native 0.57+ erweiterte Profilerstellungsfunktionen im Entwicklermodus mit React Developer Tools Profiler verfügbar. Der Profiler verwendet die experimentelle Profiler-API von React, um Zeitinformationen zu jeder gerenderten Komponente zu sammeln, um Leistungsengpässe in einer React-Anwendung zu identifizieren.

Laden Sie einfach die React Developer Tools für Ihren Browser herunter und Sie können das mitgelieferte Profiler-Tool verwenden. Der Profiler kann nur entweder im Entwicklungsmodus oder im Produktions-Profiling-Build von React v16.5+ verwendet werden. Das folgende Bild ist die Profiler-Zusammenfassung meines Blogs im Entwicklungsmodus mit React Developer Tools Profiler:

React Developer Tools Profiler-Flammendiagramm
React Developer Tools Profiler Flamegraph (große Vorschau)

Um dies zu erreichen, gehen Sie folgendermaßen vor:

  1. Laden Sie die React-Entwicklertools herunter.
  2. Stellen Sie sicher, dass sich Ihre React-Anwendung entweder im Entwicklungsmodus oder im Produktions-Profiling-Build von React v16.5+ befindet.
  3. Öffnen Sie die Registerkarte „Entwicklertools“ von Chrome. Es wird eine neue Registerkarte namens „Profiler“ verfügbar sein, die von React Developer Tools bereitgestellt wird.
  4. Klicken Sie auf die Schaltfläche „Aufzeichnen“ und führen Sie die Aktionen aus, die Sie profilieren möchten. Beenden Sie die Aufzeichnung idealerweise, nachdem Sie die Aktionen ausgeführt haben, die Sie profilieren möchten.
  5. Ein Diagramm (bekannt als Flamegraph) wird mit allen Event-Handlern und Komponenten Ihrer React-App angezeigt.

Hinweis : Weitere Informationen finden Sie in der Dokumentation.

Memoisierung mit React.memo()

React v16 wurde mit einer zusätzlichen API veröffentlicht, einer Komponente höherer Ordnung namens React.memo() . Laut Dokumentation existiert dies nur als Performance-Optimierung .

Sein Name „ Memo “ kommt von der Memoisierung, die im Grunde eine Form der Optimierung ist, die hauptsächlich zur Beschleunigung des Codes verwendet wird, indem die Ergebnisse teurer Funktionsaufrufe gespeichert und das gespeicherte Ergebnis zurückgegeben werden, wenn dieselbe teure Funktion erneut aufgerufen wird.

Memoization ist eine Technik zum einmaligen Ausführen einer Funktion, normalerweise einer reinen Funktion, und zum anschließenden Speichern des Ergebnisses im Speicher. Wenn wir versuchen, diese Funktion mit denselben Argumenten wie zuvor erneut auszuführen, wird nur das zuvor gespeicherte Ergebnis der ersten Ausführung der Funktion zurückgegeben, ohne die Funktion erneut auszuführen.

Wenn man die obige Beschreibung auf das React-Ökosystem abbildet, sind die erwähnten Funktionen React-Komponenten und die Argumente Requisiten.

Das Standardverhalten einer mit React.memo() deklarierten Komponente besteht darin, dass sie nur gerendert wird, wenn sich die Requisiten in der Komponente geändert haben. Es führt einen flachen Vergleich der Requisiten durch, um dies zu überprüfen, aber es ist eine Option verfügbar, um dies zu überschreiben.

React.memo() steigert die Leistung einer React-App, indem es das erneute Rendern von Komponenten vermeidet, deren Requisiten sich nicht geändert haben oder wenn ein erneutes Rendern nicht erforderlich ist.

Der folgende Code ist die grundlegende Syntax von React.memo() :

 const MemoizedComponent = React.memo((props) => { // Component code goes in here })

Wann React.memo() verwendet werden sollte

  • Reine Funktionskomponente
    Sie können React.memo() verwenden, wenn Ihre Komponente funktionsfähig ist, dieselben Requisiten erhält und immer dieselbe Ausgabe rendert. Sie können React.memo() auch für nicht rein funktionale Komponenten mit React-Hooks verwenden.
  • Die Komponente wird häufig gerendert
    Sie können React.memo() verwenden, um eine Komponente zu umschließen, die häufig gerendert wird.
  • Die Komponente wird mit denselben Requisiten erneut gerendert
    Verwenden Sie React.memo() , um eine Komponente zu umschließen, die normalerweise beim erneuten Rendern mit denselben Props versehen wird.
  • Mittlere bis hohe Elemente
    Verwenden Sie es für eine Komponente, die eine mittlere bis hohe Anzahl von UI-Elementen enthält, um Props auf Gleichheit zu prüfen.

Hinweis : Seien Sie vorsichtig, wenn Sie sich Komponenten merken, die Requisiten als Callbacks verwenden. Stellen Sie sicher, dass Sie zwischen den Renderings dieselbe Callback-Funktionsinstanz verwenden. Dies liegt daran, dass die übergeordnete Komponente bei jedem Rendern verschiedene Instanzen der Callback-Funktion bereitstellen könnte, wodurch der Memoisierungsprozess unterbrochen wird. Um dies zu beheben, stellen Sie sicher, dass die gespeicherte Komponente immer dieselbe Callback-Instanz erhält.

Mal sehen, wie wir die Memoisierung in einer realen Situation verwenden können. Die unten stehende Funktionskomponente namens „Foto“ verwendet React.memo() , um ein erneutes Rendern zu verhindern.

 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);

Der obige Code besteht aus einer funktionalen Komponente, die ein div anzeigt, das einen Fototitel und die Position des Motivs auf dem Foto enthält. Wir speichern die Komponente auch, indem wir eine neue Funktion erstellen und sie MemoizedPhoto . Das Speichern der Fotokomponente verhindert, dass die Komponente erneut gerendert wird, solange Requisiten, title und location bei nachfolgenden Renderings gleich sind.

 // 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" />

Hier ruft React die gespeicherte Funktion nur einmal auf. Die Komponente wird beim nächsten Aufruf nicht gerendert, solange die Requisiten gleich bleiben.

Bündelung und Minimierung

In React Single-Page-Anwendungen können wir unseren gesamten JavaScript-Code in einer einzigen Datei bündeln und minimieren. Das ist in Ordnung, solange unsere Anwendung relativ klein ist.

Wenn unsere React-Anwendung wächst, wird das Bündeln und Minimieren unseres gesamten JavaScript-Codes in einer einzigen Datei problematisch, schwer verständlich und mühsam. Es wirkt sich auch auf die Leistung und Ladezeit unserer React-App aus, da wir eine große JavaScript-Datei an den Browser senden. Wir brauchen also einen Prozess, der uns hilft, die Codebasis in verschiedene Dateien aufzuteilen und sie nach Bedarf in Intervallen an den Browser zu liefern.

In einer solchen Situation können wir eine Art Asset-Bundler wie Webpack verwenden und dann seine Code-Splitting-Funktionalität nutzen, um unsere Anwendung in mehrere Dateien aufzuteilen.

Code-Splitting wird in der Webpack-Dokumentation als Mittel zur Verbesserung der Ladezeit einer Anwendung vorgeschlagen. Es wird auch in der Dokumentation von React für Lazy-Loading vorgeschlagen (das nur die Dinge bereitstellt, die der Benutzer derzeit benötigt), was die Leistung dramatisch verbessern kann.

Webpack schlägt drei allgemeine Ansätze für das Code-Splitting vor:

  • Einstiegspunkte
    Code manuell unter Verwendung der Eingabekonfiguration aufteilen.
  • Vermeidung von Doppelarbeit
    Verwenden Sie SplitChunksPlugin , um Chunks zu deduplizieren und aufzuteilen.
  • Dynamische Importe
    Split-Code über Inline-Funktionsaufrufe innerhalb von Modulen.

Vorteile des Code-Splittings

  • Das Aufteilen von Code hilft bei den Cache-Ressourcen des Browsers und bei Code, der sich nicht oft ändert.
  • Es hilft dem Browser auch, Ressourcen parallel herunterzuladen, was die Gesamtladezeit der Anwendung verkürzt.
  • Es ermöglicht uns, Code in Chunks aufzuteilen, die bei Bedarf oder nach Bedarf von der Anwendung geladen werden.
  • Es hält das anfängliche Herunterladen von Ressourcen beim ersten Rendern relativ gering, wodurch die Ladezeit der App verkürzt wird.
Bündelungs- und Minifizierungsprozess
Bündelungs- und Minimierungsprozess (Große Vorschau)

Unveränderliche Datenstrukturen

Die Dokumentation von React spricht von der Macht, Daten nicht zu mutieren. Alle Daten, die nicht geändert werden können, sind unveränderlich. Unveränderlichkeit ist ein Konzept, das React-Programmierer verstehen sollten.

Ein unveränderlicher Wert oder ein unveränderliches Objekt kann nicht geändert werden. Wenn es also ein Update gibt, wird ein neuer Wert im Speicher erstellt, der alte bleibt unberührt.

Wir können unveränderliche Datenstrukturen und React.PureComponent , um automatisch nach einer komplexen Zustandsänderung zu suchen. Wenn beispielsweise der Zustand in Ihrer Anwendung unveränderlich ist, können Sie mit einer Zustandsverwaltungsbibliothek wie Redux tatsächlich alle Zustandsobjekte in einem einzigen Speicher speichern, sodass Sie Funktionen zum Rückgängigmachen und Wiederherstellen einfach implementieren können.

Vergessen Sie nicht, dass wir unveränderliche Daten nach ihrer Erstellung nicht mehr ändern können.

Vorteile unveränderlicher Datenstrukturen

  • Sie haben keine Nebenwirkungen.
  • Unveränderliche Datenobjekte sind einfach zu erstellen, zu testen und zu verwenden.
  • Sie helfen uns beim Schreiben von Logik, die verwendet werden kann, um schnell nach Aktualisierungen im Status zu suchen, ohne die Daten immer wieder überprüfen zu müssen.
  • Sie helfen dabei, eine zeitliche Kopplung zu verhindern (eine Art der Kopplung, bei der Code von der Ausführungsreihenfolge abhängt).

Die folgenden Bibliotheken helfen dabei, einen Satz unveränderlicher Datenstrukturen bereitzustellen:

  • Unveränderlichkeitshelfer
    Mutieren Sie eine Kopie von Daten, ohne die Quelle zu ändern.
  • Unveränderlich.js
    Unveränderliche persistente Datensammlungen für JavaScript erhöhen die Effizienz und Einfachheit.
  • nahtlos-unveränderlich
    Unveränderliche Datenstrukturen für JavaScript werden abwärtskompatibel mit normalen JavaScript-Arrays und -Objekten.
  • Reagieren-Kopieren-Schreiben
    Dies ergibt einen unveränderlichen Zustand mit einer veränderlichen API.

Andere Methoden zur Leistungssteigerung

Verwenden Sie vor der Bereitstellung einen Produktions-Build

Die Dokumentation von React schlägt vor, beim Bereitstellen Ihrer App den minimierten Produktions-Build zu verwenden.

Reagieren Sie auf die „Production Build“-Warnung der Developer Tools
Reagieren Sie auf die „Production Build“-Warnung der Entwicklertools (große Vorschau)

Vermeiden Sie anonyme Funktionen

Da anonymen Funktionen kein Bezeichner zugewiesen wird (über const/let/var ), sind sie nicht dauerhaft, wenn eine Komponente unweigerlich erneut gerendert wird. Dies bewirkt, dass JavaScript jedes Mal, wenn diese Komponente neu gerendert wird, neuen Speicher zuweist, anstatt ein einzelnes Stück Speicher nur einmal zuzuweisen, wie wenn benannte Funktionen verwendet werden.

 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> ); } }

Der obige Code zeigt zwei verschiedene Möglichkeiten, eine Schaltfläche beim Klicken eine Aktion ausführen zu lassen. Der erste Codeblock verwendet eine anonyme Funktion in der onClick() , was die Leistung beeinträchtigen würde. Der zweite Codeblock verwendet eine benannte Funktion in der Funktion onClick() , was in diesem Szenario der richtige Weg ist.

Die Montage und Demontage von Komponenten ist oft teuer

Es ist nicht ratsam, Bedingungen oder Tenäre zu verwenden, um eine Komponente verschwinden zu lassen (dh sie auszuhängen), da die zum Verschwinden gebrachte Komponente dazu führt, dass der Browser neu gezeichnet und umgeleitet wird. Dies ist ein teurer Prozess, da die Positionen und Geometrien von HTML-Elementen im Dokument neu berechnet werden müssen. Stattdessen können wir die opacity und visibility von CSS verwenden, um die Komponente auszublenden. Auf diese Weise befindet sich die Komponente immer noch im DOM, ist aber unsichtbar, ohne Leistungseinbußen.

Virtualisieren Sie lange Listen

Die Dokumentation schlägt vor, dass Sie beim Rendern einer Liste mit einer großen Datenmenge jeweils nur einen kleinen Teil der Daten in der Liste innerhalb des sichtbaren Darstellungsbereichs rendern sollten. Dann können Sie weitere Daten rendern, während die Liste gescrollt wird; Daher werden die Daten nur angezeigt, wenn sie sich im Ansichtsfenster befinden. Dieser Vorgang wird als „Windowing“ bezeichnet. Beim Windowing wird zu jedem Zeitpunkt eine kleine Teilmenge von Zeilen gerendert. Dafür gibt es beliebte Bibliotheken, von denen zwei von Brian Vaughn gepflegt werden:

  • Reaktionsfenster
  • reagieren-virtualisiert

Fazit

Es gibt mehrere andere Methoden zur Verbesserung der Leistung Ihrer React-Anwendung. Dieser Artikel hat die wichtigsten und effektivsten Methoden zur Leistungsoptimierung besprochen.

Ich hoffe, es hat Ihnen Spaß gemacht, dieses Tutorial durchzulesen. Sie können mehr über die unten aufgeführten Ressourcen erfahren. Wenn Sie Fragen haben, hinterlassen Sie diese im Kommentarbereich unten. Gerne beantworte ich jede einzelne.

Referenzen und verwandte Ressourcen

  • „Optimierung der Leistung“, React Docs
  • „Verwenden Sie React.memo mit Bedacht“, Dmitri Pavlutin
  • „Techniken zur Leistungsoptimierung in React“, Niteesh Yadav
  • „Unveränderlichkeit in der Reaktion: An mutierenden Objekten ist nichts auszusetzen“, Esteban Herrera
  • „10 Möglichkeiten zur Optimierung der Leistung Ihrer React-App“, Chidume Nnamdi
  • „5 Tipps zur Verbesserung der Leistung Ihrer React-Apps“, William Le