Kodokon kodokon.com

Suspense, lazy und Code-Splitting

Meistere den Suspension-Mechanismus, das verzögerte Laden von Komponenten und das Aufteilen des Bundles.

9 Min. · 3 Fragen

Diese Lektion in Kodokon öffnen

Suspense beruht auf einem wenig bekannten Mechanismus: Eine Komponente, die ihre Daten oder ihren Code noch nicht hat, suspendiert ihr Rendering. Unter der Haube wirft die aktuelle Implementierung ein Promise, das React genauso abfängt, wie es einen Fehler abfangen würde. React steigt dann bis zur nächsten <Suspense>-Grenze auf, zeigt deren fallback an und wiederholt dann den Render des Teilbaums, sobald das Promise aufgelöst ist. Das ist ein Implementierungsdetail (wirf niemals selbst ein Promise), aber es erklärt das gesamte beobachtbare Verhalten: Die Grenze wirkt wie ein deklaratives catch für asynchrone Arbeit. Der historische Anwendungsfall ist Code-Splitting mit React.lazy.

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

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

function Dashboard() {
  return (
    <Suspense fallback={<Spinner />}>
      <Chart points={data} />
    </Suspense>
  );
}
Der Code von Chart wird erst beim ersten Render heruntergeladen.

lazy erwartet eine Funktion, die ein Promise zurückgibt, das zu einem Modul mit einem default-Export aufgelöst wird. Das Aufteilen selbst ist Sache des Bundlers: Jeder dynamische import() wird zu einem Split-Point und erzeugt einen separaten Chunk, der bei Bedarf geladen wird. Eine häufige Feinheit in echten Codebasen: Deine Komponenten sind oft benannte Exports. Dann musst du das Modul im Vorbeigehen anpassen.

JAVASCRIPT
const Chart = lazy(() =>
  import('./charts').then((module) => ({
    default: module.Chart,
  }))
);
Einen benannten Export an den React.lazy-Vertrag anpassen.

Der heikle Teil in der Produktion: Während einer Navigation kann bereits angezeigter Inhalt erneut suspendieren (neue Route, neuer Chunk) und abrupt durch den Fallback ersetzt werden. Seit React 18 ändert ein in startTransition verpacktes Update dieses Verhalten: React behält die bisherige Oberfläche auf dem Bildschirm, während der neue Render im Hintergrund suspendiert, und zeigt den Fallback nur für Inhalt, der noch nie sichtbar war. Grenzen lassen sich auch in der Tiefe kombinieren: Mehrere verschachtelte <Suspense>-Grenzen erlauben dir, die Oberfläche Zone für Zone aufzudecken, vom globalen Skelett bis hinunter zu den Details.

JSX
const [isPending, startTransition] = useTransition();

function openTab(nextTab) {
  startTransition(() => {
    setTab(nextTab);
  });
}
// The current tab stays visible while the new
// one loads, instead of dropping back to the fallback.
Eine Transition verhindert das Zurückfallen auf den Fallback während der Navigation.

Wissenscheck

Stelle sicher, dass du die wichtigsten Punkte dieser Lektion behalten hast.

  1. Was muss die an React.lazy übergebene Funktion zurückgeben?
    • Direkt eine React-Komponente
    • Ein Promise, das zu einem Modul mit einem Default-Export aufgelöst wird
    • Ein Objekt { component, loading }
    • Einen String mit dem Dateipfad
  2. Wie verhinderst du, dass bereits angezeigter Inhalt durch den Fallback ersetzt wird, wenn eine Navigation suspendiert?
    • Das State-Update in startTransition verpacken
    • fallback={null} an die Suspense-Grenze übergeben
    • useEffect nutzen, um die Navigation zu verzögern
    • Die übergeordnete Suspense-Grenze entfernen
  3. Wer nimmt tatsächlich die Aufteilung des Codes in separate Chunks vor?
    • React, während der Commit-Phase
    • Der Browser, über den HTTP-Cache
    • Der Bundler, ausgehend von jedem dynamischen import()