Codeaufteilung mit dynamischen Importen in Next.js

So beschleunigen Sie Ihre Next.js-App mit Code-Splitting und intelligenten Ladestrategien.

In diesem Beitrag werden verschiedene Arten der Code-Spaltung und die Verwendung dynamischer Importe zur Beschleunigung Ihrer Next.js-Anwendungen erläutert.

Routen- und komponentenbasierte Code-Splitting

Standardmäßig teilt Next.js Ihren JavaScript-Code für jede Route in separate Chunks auf. Wenn Nutzer Ihre Anwendung laden, sendet Next.js nur den Code, der für die erste Route erforderlich ist. Wenn Nutzer sich in der Anwendung bewegen, werden die mit den anderen Routen verknüpften Chunks abgerufen. Durch die routenbasierte Code-Spaltung wird die Menge an Script minimiert, die gleichzeitig geparst und kompiliert werden muss. Das führt zu kürzeren Seitenladezeiten.

Die routenbasierte Code-Spaltung ist eine gute Standardeinstellung. Sie können den Ladevorgang jedoch mit der Code-Spaltung auf Komponentenebene weiter optimieren. Wenn Ihre App große Komponenten enthält, sollten Sie diese in separate Blöcke aufteilen. So können alle großen Komponenten, die nicht kritisch sind oder nur bei bestimmten Nutzerinteraktionen (z. B. Klicken auf eine Schaltfläche) gerendert werden, mit Lazy Loading geladen werden.

Next.js unterstützt dynamische import(), mit denen Sie JavaScript-Module (einschließlich React-Komponenten) dynamisch importieren und jeden Import als separaten Chunk laden können. So können Sie den Code auf Komponentenebene aufteilen und das Ressourcenladen so steuern, dass Nutzer nur den Code herunterladen, den sie für den gerade angezeigten Teil der Website benötigen. In Next.js werden diese Komponenten standardmäßig serverseitig gerendert (SSR).

Dynamische Importe in Aktion

Dieser Beitrag enthält mehrere Versionen einer Beispiel-App, die aus einer einfachen Seite mit einer Schaltfläche besteht. Wenn Sie auf die Schaltfläche klicken, sehen Sie einen niedlichen Welpen. In den einzelnen Versionen der App sehen Sie, wie sich dynamische Importe von statischen Importen unterscheiden und wie Sie damit arbeiten.

In der ersten Version der App lebt der Welpe in components/Puppy.js. Um den Welpen auf der Seite anzuzeigen, importiert die App die Puppy-Komponente in index.js mit einer statischen Importanweisung:

import Puppy from "../components/Puppy";

Wenn Sie sehen möchten, wie Next.js die App bündelt, prüfen Sie den Netzwerk-Trace in den DevTools:

  1. Wenn Sie sich eine Vorschau der Website ansehen möchten, drücken Sie App ansehen und dann Vollbild Vollbild.

  2. Drücken Sie „Strg + Umschalttaste + J“ (oder „Befehlstaste + Optionstaste + J“ auf einem Mac), um die Entwicklertools zu öffnen.

  3. Klicken Sie auf den Tab Netzwerk.

  4. Klicken Sie das Kästchen Cache deaktivieren an.

  5. Lade die Seite neu.

Wenn Sie die Seite laden, wird der gesamte erforderliche Code, einschließlich der Puppy.js-Komponente, in index.js gebündelt:

Der Tab „Netzwerk“ in den DevTools mit sechs JavaScript-Dateien: index.js, app.js, webpack.js, main.js, 0.js und die DLL-Datei (Dynamic Link Library).

Wenn Sie auf die Schaltfläche Click me (Klicken Sie auf mich) klicken, wird dem Tab Netzwerk nur die Anfrage für das JPEG des Hundes hinzugefügt:

Der Tab „Netzwerk“ in den DevTools nach dem Klicken auf die Schaltfläche, mit denselben sechs JavaScript-Dateien und einem Bild

Der Nachteil dieses Ansatzes besteht darin, dass Nutzer die Puppy-Komponente laden müssen, auch wenn sie nicht auf die Schaltfläche klicken, um den Welpen zu sehen, da sie in index.js enthalten ist. In diesem kleinen Beispiel ist das kein Problem, aber in realen Anwendungen ist es oft eine enorme Verbesserung, große Komponenten nur bei Bedarf zu laden.

Sehen wir uns nun eine zweite Version der App an, in der der statische Import durch einen dynamischen Import ersetzt wird. Next.js enthält next/dynamic, wodurch dynamische Importe für alle Komponenten in Next verwendet werden können:

import Puppy from "../components/Puppy";
import dynamic from "next/dynamic";

// ...

const Puppy = dynamic(import("../components/Puppy"));

Folgen Sie der Anleitung im ersten Beispiel, um den Netzwerk-Trace zu prüfen.

Beim ersten Laden der App wird nur index.js heruntergeladen. Diesmal ist sie 0,5 KB kleiner (von 37,9 KB auf 37,4 KB), da sie den Code für die Puppy-Komponente nicht enthält:

Im DevTools-Netzwerk werden dieselben sechs JavaScript-Dateien angezeigt, mit der Ausnahme, dass index.js jetzt 0,5 KB kleiner ist.

Die Puppy-Komponente befindet sich jetzt in einem separaten Chunk, 1.js, der nur geladen wird, wenn Sie auf die Schaltfläche klicken:

Der Tab „Netzwerk“ in den DevTools nach dem Klicken auf die Schaltfläche. Die zusätzliche Datei „1.js“ und das Bild sind unten in der Dateiliste zu sehen.

In der Praxis sind Komponenten oft viel größer. Durch das Lazy-Laden können Sie die anfängliche JavaScript-Nutzlast um Hunderte von Kilobytes reduzieren.

Dynamische Importe mit benutzerdefinierter Ladeanzeige

Wenn Sie Ressourcen mit Lazy Loading laden, sollten Sie für den Fall von Verzögerungen einen Lade-Indikator einblenden. In Next.js können Sie dazu der dynamic()-Funktion ein zusätzliches Argument übergeben:

const Puppy = dynamic(() => import("../components/Puppy"), {
  loading
: () => <p>Loading...</p>
});

Wenn Sie den Ladebalken in Aktion sehen möchten, simulieren Sie in den DevTools eine langsame Netzwerkverbindung:

  1. Wenn Sie sich eine Vorschau der Website ansehen möchten, drücken Sie App ansehen und dann Vollbild Vollbild.

  2. Drücken Sie „Strg + Umschalttaste + J“ (oder „Befehlstaste + Optionstaste + J“ auf einem Mac), um die Entwicklertools zu öffnen.

  3. Klicken Sie auf den Tab Netzwerk.

  4. Klicken Sie das Kästchen Cache deaktivieren an.

  5. Wählen Sie in der Drop-down-Liste Throttling die Option Schnelles 3G aus.

  6. Drücken Sie die Schaltfläche Klicken Sie auf mich.

Wenn Sie jetzt auf die Schaltfläche klicken, dauert es eine Weile, bis die Komponente geladen ist. In der Zwischenzeit wird in der App die Meldung „Wird geladen…“ angezeigt.

Ein dunkler Bildschirm mit dem Text

Dynamische Importe ohne SSR

Wenn eine Komponente nur clientseitig gerendert werden soll (z. B. ein Chat-Widget), können Sie die Option ssr auf false setzen:

const Puppy = dynamic(() => import("../components/Puppy"), {
  ssr
: false,
});

Fazit

Durch die Unterstützung dynamischer Importe bietet Next.js die Codeaufteilung auf Komponentenebene, wodurch die JavaScript-Nutzlasten minimiert und die Ladezeit der Anwendung verbessert werden kann. Alle Komponenten werden standardmäßig serverseitig gerendert. Sie können diese Option bei Bedarf deaktivieren.