Verwendung von Server- und Client-Komponenten
Standardmäßig sind Layouts und Seiten Server-Komponenten (Server Components), was Ihnen ermöglicht, Daten abzurufen und Teile Ihrer Benutzeroberfläche auf dem Server zu rendern, das Ergebnis optional zwischenzuspeichern und es an den Client zu streamen. Wenn Sie Interaktivität oder Browser-APIs benötigen, können Sie Client-Komponenten (Client Components) verwenden, um Funktionalität hinzuzufügen.
Diese Seite erklärt, wie Server- und Client-Komponenten in Next.js funktionieren und wann sie verwendet werden sollten, mit Beispielen, wie sie in Ihrer Anwendung kombiniert werden können.
Wann sollten Server- und Client-Komponenten verwendet werden?
Die Client- und Server-Umgebungen haben unterschiedliche Fähigkeiten. Server- und Client-Komponenten ermöglichen es Ihnen, Logik je nach Anwendungsfall in der jeweiligen Umgebung auszuführen.
Verwenden Sie Client-Komponenten, wenn Sie benötigen:
- State (Zustand) und Event-Handler (Ereignisbehandlungen). Z.B.
onClick
,onChange
. - Lifecycle-Logik (Lebenszykluslogik). Z.B.
useEffect
. - Browser-spezifische APIs. Z.B.
localStorage
,window
,Navigator.geolocation
, etc. - Custom Hooks (Benutzerdefinierte Hooks).
Verwenden Sie Server-Komponenten, wenn Sie benötigen:
- Daten aus Datenbanken oder APIs nahe der Quelle abzurufen.
- API-Schlüssel, Tokens und andere Geheimnisse zu verwenden, ohne sie dem Client preiszugeben.
- Die Menge an JavaScript, die an den Browser gesendet wird, zu reduzieren.
- Die First Contentful Paint (FCP) zu verbessern und Inhalte progressiv an den Client zu streamen.
Beispielsweise ist die <Page>
-Komponente eine Server-Komponente, die Daten über einen Beitrag abruft und sie als Props an die <LikeButton>
weitergibt, die die clientseitige Interaktivität handhabt.
Wie funktionieren Server- und Client-Komponenten in Next.js?
Auf dem Server
Auf dem Server verwendet Next.js React-APIs, um das Rendering zu orchestrieren. Die Rendering-Arbeit wird in Abschnitte unterteilt, nach einzelnen Routensegmenten (Layouts und Seiten):
- Server-Komponenten werden in ein spezielles Datenformat namens React Server Component Payload (RSC Payload) gerendert.
- Client-Komponenten und der RSC Payload werden verwendet, um HTML vorzurrendern (prerender).
Was ist der React Server Component Payload (RSC)?
Der RSC Payload ist eine kompakte binäre Darstellung des gerenderten React Server Components-Baums. Er wird von React auf dem Client verwendet, um das DOM des Browsers zu aktualisieren. Der RSC Payload enthält:
- Das gerenderte Ergebnis von Server-Komponenten
- Platzhalter für die Stellen, an denen Client-Komponenten gerendert werden sollten, und Referenzen zu ihren JavaScript-Dateien
- Alle Props, die von einer Server-Komponente an eine Client-Komponente übergeben werden
Auf dem Client (erster Ladevorgang)
Dann, auf dem Client:
- HTML wird verwendet, um dem Benutzer sofort eine schnelle, nicht-interaktive Vorschau der Route anzuzeigen.
- RSC Payload wird verwendet, um die Bäume der Client- und Server-Komponenten abzugleichen.
- JavaScript wird verwendet, um Client-Komponenten zu hydratisieren und die Anwendung interaktiv zu machen.
Was ist Hydratisierung?
Hydratisierung ist der Prozess von React, um Event-Handler an das DOM anzuhängen und das statische HTML interaktiv zu machen.
Nachfolgende Navigationen
Bei nachfolgenden Navigationen:
- Der RSC Payload wird im Voraus abgerufen und zwischengespeichert, um eine sofortige Navigation zu ermöglichen.
- Client-Komponenten werden vollständig auf dem Client gerendert, ohne das server-seitig gerenderte HTML.
Beispiele
Verwendung von Client-Komponenten
Sie können eine Client-Komponente erstellen, indem Sie die "use client"
-Direktive oben in der Datei, über Ihren Imports, hinzufügen.
"use client"
wird verwendet, um eine Grenze zwischen den Server- und Client-Modulgraphen (Bäumen) zu deklarieren.
Sobald eine Datei mit "use client"
markiert ist, werden alle ihre Imports und untergeordneten Komponenten als Teil des Client-Bundles betrachtet. Das bedeutet, Sie müssen die Direktive nicht jeder Komponente hinzufügen, die für den Client bestimmt ist.
Reduzierung der JS-Bundle-Größe
Um die Größe Ihrer Client-JavaScript-Bundles zu reduzieren, fügen Sie 'use client'
zu bestimmten interaktiven Komponenten hinzu, anstatt große Teile Ihrer Benutzeroberfläche als Client-Komponenten zu markieren.
Beispielsweise enthält die <Layout>
-Komponente hauptsächlich statische Elemente wie ein Logo und Navigationslinks, aber eine interaktive Suchleiste. <Search />
ist interaktiv und muss eine Client-Komponente sein, der Rest des Layouts kann jedoch eine Server-Komponente bleiben.
Übergeben von Daten von Server- zu Client-Komponenten
Sie können Daten von Server-Komponenten an Client-Komponenten über Props übergeben.
Alternativ können Sie Daten von einer Server-Komponente an eine Client-Komponente mit dem use
-Hook streamen. Siehe ein Beispiel.
Gut zu wissen: Props, die an Client-Komponenten übergeben werden, müssen von React serialisierbar sein.
Verschachteln von Server- und Client-Komponenten
Sie können Server-Komponenten als Prop an eine Client-Komponente übergeben. Dies ermöglicht es Ihnen, server-seitig gerenderte UI visuell innerhalb von Client-Komponenten zu verschachteln.
Ein gängiges Muster ist die Verwendung von children
, um einen Slot in einer <ClientComponent>
zu erstellen. Zum Beispiel eine <Cart>
-Komponente, die Daten auf dem Server abruft, innerhalb einer <Modal>
-Komponente, die Client-State verwendet, um die Sichtbarkeit zu steuern.
Dann können Sie in einer übergeordneten Server-Komponente (z.B. <Page>
) eine <Cart>
als Kind der <Modal>
übergeben:
In diesem Muster werden alle Server-Komponenten im Voraus auf dem Server gerendert, einschließlich derer, die als Props übergeben werden. Der resultierende RSC Payload enthält Referenzen, wo Client-Komponenten innerhalb des Komponentenbaums gerendert werden sollten.
Context-Provider
React Context wird häufig verwendet, um globalen State wie das aktuelle Theme zu teilen. Allerdings wird React Context in Server-Komponenten nicht unterstützt.
Um Context zu verwenden, erstellen Sie eine Client-Komponente, die children
akzeptiert:
Dann importieren Sie es in eine Server-Komponente (z.B. layout
):
Ihre Server-Komponente kann nun Ihren Provider direkt rendern, und alle anderen Client-Komponenten in Ihrer App können diesen Context nutzen.
Gut zu wissen: Sie sollten Provider so tief wie möglich im Baum rendern – beachten Sie, dass
ThemeProvider
nur{children}
umschließt und nicht das gesamte<html>
-Dokument. Dies erleichtert es Next.js, die statischen Teile Ihrer Server-Komponenten zu optimieren.
Drittanbieter-Komponenten
Wenn Sie eine Drittanbieter-Komponente verwenden, die auf client-spezifischen Funktionen basiert, können Sie sie in eine Client-Komponente einwickeln, um sicherzustellen, dass sie wie erwartet funktioniert.
Zum Beispiel kann die <Carousel />
aus dem acme-carousel
-Paket importiert werden. Diese Komponente verwendet useState
, hat aber noch keine "use client"
-Direktive.
Wenn Sie <Carousel />
innerhalb einer Client-Komponente verwenden, funktioniert sie wie erwartet:
Wenn Sie jedoch versuchen, sie direkt innerhalb einer Server-Komponente zu verwenden, erhalten Sie einen Fehler. Das liegt daran, dass Next.js nicht weiß, dass <Carousel />
client-spezifische Funktionen verwendet.
Um dies zu beheben, können Sie Drittanbieter-Komponenten, die auf client-spezifischen Funktionen basieren, in Ihre eigenen Client-Komponenten einwickeln:
Jetzt können Sie <Carousel />
direkt innerhalb einer Server-Komponente verwenden:
Tipp für Bibliotheksautoren
Wenn Sie eine Komponentenbibliothek erstellen, fügen Sie die
"use client"
-Direktive zu Einstiegspunkten hinzu, die auf client-spezifischen Funktionen basieren. Dies ermöglicht es Ihren Benutzern, Komponenten in Server-Komponenten zu importieren, ohne Wrapper erstellen zu müssen.Es ist erwähnenswert, dass einige Bundler
"use client"
-Direktiven entfernen könnten. Ein Beispiel, wie Sie esbuild konfigurieren können, um die"use client"
-Direktive einzubeziehen, finden Sie in den Repositories React Wrap Balancer und Vercel Analytics.
Vermeidung von Umgebungskontamination
JavaScript-Module können sowohl von Server- als auch von Client-Komponenten gemeinsam genutzt werden. Das bedeutet, dass versehentlich serverseitiger Code in den Client importiert werden kann. Betrachten Sie beispielsweise die folgende Funktion:
Diese Funktion enthält einen API_KEY
, der niemals dem Client zugänglich gemacht werden sollte.
In Next.js werden nur Umgebungsvariablen, die mit NEXT_PUBLIC_
beginnen, in das Client-Bundle aufgenommen. Wenn Variablen nicht mit diesem Präfix versehen sind, ersetzt Next.js sie durch einen leeren String.
Folglich wird getData()
, selbst wenn es im Client importiert und ausgeführt wird, nicht wie erwartet funktionieren.
Um versehentliche Verwendung in Client-Komponenten zu verhindern, können Sie das server-only
-Paket verwenden.
Importieren Sie dann das Paket in eine Datei, die serverseitigen Code enthält:
Wenn Sie nun versuchen, das Modul in eine Client-Komponente zu importieren, wird ein Build-Time-Fehler auftreten.
Gut zu wissen: Das entsprechende
client-only
-Paket kann verwendet werden, um Module zu kennzeichnen, die clientseitige Logik enthalten, wie z.B. Code, der auf daswindow
-Objekt zugreift.