Codeaufteilung mit React.lazy und Suspense

Sie sollten Nutzern nie mehr Code als nötig senden. Teilen Sie Ihre Bundles also auf, um das zu vermeiden.

Mit der Methode React.lazy lässt sich eine React-Anwendung mithilfe dynamischer Importe ganz einfach auf Komponentenebene aufteilen.

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, Hilfsmethoden und Bibliotheken von Drittanbietern. Wenn nicht versucht wird, 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. Das kann sich erheblich auf die Seitenleistung auswirken.

Die React.lazy-Funktion bietet eine integrierte Möglichkeit, Komponenten in einer Anwendung mit sehr wenig Aufwand in separate JavaScript-Chunks aufzuteilen. Sie können sich dann um das Laden von Status kümmern, wenn Sie die Komponente mit Suspense kombinieren.

Spannung

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

Es wird jedoch immer eine leichte Verzögerung geben, die Nutzer erleben müssen, wenn eine codeaufgeteilte Komponente über das Netzwerk abgerufen wird. Daher ist es wichtig, einen nützlichen Ladestatus anzuzeigen. Dieses Problem lässt sich mit React.lazy und der Komponente Suspense 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 nur gerendert, wenn auf die Schaltfläche geklickt wird. Dann wird eine Anfrage gesendet, um den für das gesperrte AvatarComponent erforderlichen Code abzurufen. In der Zwischenzeit wird die Fallback-Ladekomponente angezeigt.

Der Code, aus dem AvatarComponent besteht, ist kurz, weshalb der Ladestatus nur kurz angezeigt wird. Das Laden größerer Komponenten kann viel länger dauern, insbesondere bei schwachen Netzwerkverbindungen.

So können Sie besser nachvollziehen, wie das funktioniert:

  • Wenn Sie sich eine Vorschau der Website ansehen möchten, drücken Sie App ansehen und dann 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, das standardmäßig auf Keine Drosselung eingestellt ist. Wählen Sie 3G – schnell aus.
  • Klicken Sie in der App auf die Schaltfläche Click Me (Klick mich).

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

DevTools-Netzwerkbereich mit einer chunk.js-Datei, die heruntergeladen wird

Mehrere Komponenten sperren

Eine weitere Funktion von Suspense ist, dass Sie das Laden mehrerer Komponenten pausieren können, auch wenn sie alle verzögert 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 Methode, um das Rendern mehrerer Komponenten zu verzögern und gleichzeitig nur einen einzelnen Ladestatus anzuzeigen. Sobald alle Komponenten abgerufen wurden, werden sie dem Nutzer gleichzeitig angezeigt.

Das sehen Sie im folgenden eingebetteten Video:

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

Fehler beim Laden beheben

Mit Suspense können Sie einen temporären Ladestatus anzeigen, während im Hintergrund Netzwerkanfragen gestellt werden. Was aber, wenn diese Netzwerkanfragen aus irgendeinem Grund fehlschlagen? Möglicherweise sind Sie offline oder Ihre Web-App versucht, eine versionierte URL, die veraltet ist und nach einer Serverneubereitstellung nicht mehr verfügbar ist, verzögert zu laden.

React bietet ein Standardmuster für die ordnungsgemäße Behandlung dieser Arten von Ladefehlern: 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.

Um Fehler beim Lazy Loading zu erkennen und zu beheben, können Sie Ihre Suspense-Komponente in eine übergeordnete Komponente einfügen, die als Fehlergrenze dient. In der render()-Methode der Fehlergrenze können Sie die untergeordneten Elemente unverändert rendern, wenn kein Fehler auftritt, oder eine benutzerdefinierte Fehlermeldung rendern, 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 sich nicht sicher sind, wo Sie mit der Codeaufteilung in Ihrer React-Anwendung beginnen sollen, gehen Sie so vor:

  1. Beginnen Sie auf Routenebene. Routen sind die einfachste Möglichkeit, Punkte in Ihrer Anwendung zu identifizieren, die aufgeteilt werden können. In der React-Dokumentation wird beschrieben, wie Suspense zusammen mit react-router verwendet werden kann.
  2. Suchen Sie auf einer Seite Ihrer Website nach großen Komponenten, die nur bei bestimmten Nutzerinteraktionen (z. B. beim Klicken auf eine Schaltfläche) gerendert werden. Durch die Aufteilung dieser Komponenten wird die Größe Ihrer JavaScript-Nutzlasten minimiert.
  3. Teilen Sie alle anderen Elemente auf, die nicht auf dem Bildschirm zu sehen und für den Nutzer nicht wichtig sind.