Codeaufteilung mit React.lazy und Suspense

Sie sollten Ihren Nutzern nie mehr Code als nötig senden. Teilen Sie Ihre Bundles daher auf, damit das nicht passiert.

Mit der Methode React.lazy können Sie eine React-Anwendung mithilfe dynamischer Importe ganz einfach auf Komponentenebene codieren.

import React, { lazy } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));

const DetailsComponent = () => (
  <div>
    <AvatarComponent />
  </div>
)

Welchen Nutzen bieten sie?

Eine große React-Anwendung besteht in der Regel aus vielen Komponenten, Dienstprogrammmethoden und Bibliotheken von Drittanbietern. Wenn nicht versucht wird, verschiedene Teile einer Anwendung nur dann zu laden, wenn sie benötigt werden, wird ein einzelnes großes JavaScript-Bundle an Ihre Nutzer gesendet, sobald sie die erste Seite laden. Das kann sich erheblich auf die Seitenleistung auswirken.

Die React.lazy-Funktion bietet eine integrierte Möglichkeit, Komponenten in einer Anwendung mit wenig Aufwand in separate JavaScript-Chunks zu unterteilen. Sie können dann für den Ladestatus sorgen, wenn Sie ihn mit der Komponente Suspense koppeln.

Spannung

Das Problem beim Senden einer großen JavaScript-Nutzlast an Nutzer ist die lange Ladezeit der Seite, insbesondere auf leistungsschwächeren Geräten und Netzwerkverbindungen. Aus diesem Grund sind Code-Splitting und Lazy Loading äußerst nützlich.

Es gibt jedoch immer eine kleine Verzögerung, wenn eine Code-Split-Komponente über das Netzwerk abgerufen wird. Daher ist es wichtig, einen nützlichen Ladestatus anzuzeigen. Mit React.lazy und der Komponente Suspense lässt sich dieses Problem beheben.

import React, { lazy, Suspense } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));

const renderLoader = () => <p>Loading</p>;

const DetailsComponent = () => (
  <Suspense fallback={renderLoader()}>
    <AvatarComponent />
  </Suspense>
)

Suspense akzeptiert eine fallback-Komponente, mit der Sie eine beliebige React-Komponente als Ladestatus anzeigen können. Das folgende Beispiel zeigt, wie das funktioniert. Der Avatar wird erst gerendert, wenn auf die Schaltfläche geklickt und dann eine Anfrage zum Abrufen des Codes für das gesperrte AvatarComponent gesendet wird. In der Zwischenzeit wird die Fallback-Ladekomponente angezeigt.

Hier ist der Code, aus dem AvatarComponent besteht, klein, weshalb das Ladesymbol nur für kurze Zeit angezeigt wird. Bei größeren Komponenten kann das Laden viel länger dauern, insbesondere bei schwachen Netzwerkverbindungen.

Zur Veranschaulichung:

  • Wenn Sie sich eine Vorschau der Website ansehen möchten, klicken Sie auf App ansehen und dann auf Vollbild Vollbild.
  • Drücken Sie Strg + Umschalttaste + J (oder Befehlstaste + Optionstaste + J auf einem Mac), um die Entwicklertools zu öffnen.
  • Klicken Sie auf den Tab Netzwerk.
  • Klicken Sie auf das Drop-down-Menü Drosselung. Standardmäßig ist Keine Drosselung ausgewählt. Wählen Sie Schnelles 3G aus.
  • Klicken Sie in der App auf die Schaltfläche Click Me.

Die Ladeanzeige wird jetzt länger angezeigt. Beachten Sie, dass der gesamte Code, aus dem die AvatarComponent besteht, als separater Teil abgerufen wird.

Netzwerkbereich in Entwicklertools, in dem eine Chunk.js-Datei heruntergeladen wird

Mehrere Komponenten pausieren

Mit Suspense können Sie außerdem das Laden mehrerer Komponenten pausieren, auch wenn sie alle mit Lazy Loading geladen werden.

Beispiel:

import React, { lazy, Suspense } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));
const InfoComponent = lazy(() => import('./InfoComponent'));
const MoreInfoComponent = lazy(() => import('./MoreInfoComponent'));

const renderLoader = () => <p>Loading</p>;

const DetailsComponent = () => (
  <Suspense fallback={renderLoader()}>
    <AvatarComponent />
    <InfoComponent />
    <MoreInfoComponent />
  </Suspense>
)

Dies ist eine äußerst nützliche Möglichkeit, das Rendern mehrerer Komponenten zu verzögern und dabei nur einen einzigen Ladestatus anzuzeigen. Sobald alle Komponenten abgerufen wurden, können sie für den Nutzer gleichzeitig angezeigt werden.

Sie können dies mit folgender Einbettung sehen:

Andernfalls kann es leicht zu einem gestupften Laden kommen, bei dem verschiedene Teile einer Benutzeroberfläche nacheinander geladen werden und jeder eine eigene Ladeanzeige hat. Das kann die Nutzerfreundlichkeit beeinträchtigen.

Fehler beim Laden beheben

Mit Suspense können Sie einen vorübergehenden Ladestatus anzeigen lassen, während im Hintergrund Netzwerkanfragen gesendet werden. Was aber, wenn diese Netzwerkanfragen aus irgendeinem Grund fehlschlagen? Möglicherweise sind Sie offline oder Ihre Webanwendung versucht, eine versionierte URL per Lazy Loading zu laden, die veraltet und nach einer erneuten Serverbereitstellung nicht mehr verfügbar ist.

React bietet ein Standardmuster für die fehlerfreie Behandlung dieser Art von Ladefehlern: die Verwendung einer Fehlergrenze. Wie in der Dokumentation beschrieben, kann jede React-Komponente als Fehlergrenze dienen, wenn sie eine oder beide Lebenszyklusmethoden static getDerivedStateFromError() oder componentDidCatch() implementiert.

Zum Erkennen und Beheben von Lazy Loading-Fehlern können Sie die Suspense-Komponente mit einer übergeordneten Komponente zusammenfassen, die als Fehlergrenze dient. Innerhalb der render()-Methode der Fehlergrenze können Sie die untergeordneten Elemente unverändert rendern, wenn kein Fehler auftritt, oder eine benutzerdefinierte Fehlermeldung, wenn etwas schiefgeht:

import React, { lazy, Suspense } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));
const InfoComponent = lazy(() => import('./InfoComponent'));
const MoreInfoComponent = lazy(() => import('./MoreInfoComponent'));

const renderLoader = () => <p>Loading</p>;

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {hasError: false};
  }

  static getDerivedStateFromError(error) {
    return {hasError: true};
  }

  render() {
    if (this.state.hasError) {
      return <p>Loading failed! Please reload.</p>;
    }

    return this.props.children;
  }
}

const DetailsComponent = () => (
  <ErrorBoundary>
    <Suspense fallback={renderLoader()}>
      <AvatarComponent />
      <InfoComponent />
      <MoreInfoComponent />
    </Suspense>
  </ErrorBoundary>
)

Fazit

Wenn Sie nicht sicher sind, wo Sie die Codeaufteilung auf Ihre React-Anwendung anwenden sollen, gehen Sie so vor:

  1. Beginnen Sie auf Routenebene. Mithilfe von Routen können Sie am einfachsten Stellen in Ihrer Anwendung identifizieren, die aufgeteilt werden können. In den React-Dokumenten wird gezeigt, wie Suspense zusammen mit react-router verwendet werden kann.
  2. Ermitteln Sie alle großen Komponenten auf einer Seite Ihrer Website, die nur bei bestimmten Nutzerinteraktionen (z. B. Klicken auf eine Schaltfläche) gerendert werden. Durch die Aufteilung dieser Komponenten werden Ihre JavaScript-Nutzlasten minimiert.
  3. Alles andere, was nicht auf dem Bildschirm zu sehen ist und für den Nutzer nicht wichtig ist, sollte aufgeteilt werden.