Codeaufteilung mit React.lazy und Suspense

Sie müssen nie mehr Code an Ihre Nutzer versenden als nötig. Teilen Sie also Ihre Sets auf, damit dies nie passiert.

Mit der Methode React.lazy kann eine React-Anwendung mithilfe dynamischer Importe einfach auf Komponentenebene aufgeteilt werden.

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 Drittanbieterbibliotheken. Wenn Sie nicht versuchen, verschiedene Teile einer Anwendung nur bei Bedarf zu laden, wird ein einzelnes, großes JavaScript-Bundle an Ihre Nutzer gesendet, sobald sie die erste Seite laden. Dies kann die Seitenleistung erheblich beeinträchtigen.

Die Funktion React.lazy bietet eine integrierte Möglichkeit, Komponenten in einer Anwendung mit geringem Aufwand in separate JavaScript-Blöcke aufzuteilen. Sie können dann die Ladestatus übernehmen, wenn Sie sie mit der Suspense-Komponente koppeln.

Suspense

Das Problem beim Versenden einer großen JavaScript-Nutzlast an Nutzer ist die Zeit, die zum vollständigen Laden der Seite benötigt wird, insbesondere auf schwächeren Geräten und Netzwerkverbindungen. Aus diesem Grund sind Codeaufteilung und Lazy Loading äußerst nützlich.

Es kann jedoch immer zu einer leichten Verzögerung kommen, wenn eine Komponente zur Codeaufteilung über das Netzwerk abgerufen wird. Daher ist es wichtig, einen nützlichen Ladestatus anzuzeigen. Durch die Verwendung von React.lazy mit der Komponente Suspense lässt sich dieses Problem lösen.

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 jede React-Komponente als Ladestatus anzeigen können. Das folgende Beispiel zeigt, wie das funktioniert. Der Avatar wird nur gerendert, wenn auf die Schaltfläche geklickt wird. Dabei wird dann eine Anfrage gesendet, um den für das gesperrten AvatarComponent erforderlichen Code abzurufen. In der Zwischenzeit wird die Fallback-Ladekomponente gezeigt.

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

Um die Funktionsweise besser zu veranschaulichen:

  • Um die Website als Vorschau anzusehen, wählen Sie App ansehen und dann Vollbild Vollbild aus.
  • Drücken Sie Strg + Umschalttaste + J (oder Befehlstaste + Option + J auf dem Mac), um die Entwicklertools zu öffnen.
  • Klicken Sie auf den Tab Netzwerk.
  • Klicken Sie auf das Drop-down-Menü Drosselung, das standardmäßig auf Keine Drosselung eingestellt ist. Wählen Sie Schnelles 3G aus.
  • Klicken Sie in der App auf die Schaltfläche Hier klicken.

Die Ladeanzeige wird jetzt länger eingeblendet. Wie Sie sehen, wird der gesamte Code, aus dem AvatarComponent besteht, als separater Block abgerufen.

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

Mehrere Komponenten anhalten

Ein weiteres Feature von Suspense besteht darin, dass Sie damit mehrere Komponenten aussetzen können, auch wenn sie alle Lazy Loading sind.

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 Methode, um das Rendering mehrerer Komponenten zu verzögern, während nur ein einziger Ladestatus angezeigt wird. Sobald alle Komponenten abgerufen sind, werden dem Nutzer alle gleichzeitig angezeigt.

Sie können dies an der folgenden Einbettung sehen:

Andernfalls kann es leicht passieren, dass ein gestaffeltes Laden auftritt oder wenn verschiedene Teile einer UI nacheinander geladen werden, wobei jeder Teil eine eigene Ladeanzeige hat. Dies kann dazu führen, dass die User Experience noch unklarer wird.

Ladefehler beheben

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

React hat ein Standardmuster für die reibungslose Verarbeitung solcher Ladefehler: die Verwendung einer Fehlergrenze. Wie in der Dokumentation beschrieben, kann jede React-Komponente als Fehlergrenze dienen, wenn sie eine (oder beide) der Lebenszyklusmethoden static getDerivedStateFromError() oder componentDidCatch() implementiert.

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

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. Routen sind die einfachste Möglichkeit, um Punkte Ihrer Anwendung zu identifizieren, die aufgeteilt werden können. In der React-Dokumentation 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 das Aufteilen dieser Komponenten werden die JavaScript-Nutzlasten minimiert.
  3. Alle anderen Elemente, die nicht auf dem Bildschirm zu sehen sind und für den Nutzer nicht kritisch sind, sollten aufteilen.