00:00:00(fröhliche Musik) - Okay, hallo zusammen.
00:00:06Mein Name ist Aurora.
00:00:07Ich bin Webentwicklerin aus Norwegen.
00:00:09Ich arbeite als Beraterin bei Crane Consulting und entwickle aktiv mit dem Next.js App Router in meinem aktuellen Beratungsprojekt.
00:00:16Heute zeige ich Ihnen Muster für Komposition,
00:00:18Caching und Architektur in modernem Next.js,
00:00:21die Ihnen helfen,
00:00:22Skalierbarkeit und Leistung zu gewährleisten.
00:00:24Lassen Sie mich zunächst das grundlegendste Konzept für diesen Vortrag auffrischen: statisches und dynamisches Rendering.
00:00:30Wir sind beiden im Next.js App Router begegnet.
00:00:33Statisches Rendering ermöglicht uns den Bau schnellerer Websites,
00:00:36da vorgerenderte Inhalte zwischengespeichert und global verteilt werden können,
00:00:39sodass Benutzer schneller darauf zugreifen können.
00:00:42Zum Beispiel die Next.js Conf-Website.
00:00:46Statisches Rendering reduziert die Serverlast,
00:00:48da Inhalte nicht für jede Benutzeranfrage neu generiert werden müssen.
00:00:51Vorgerenderte Inhalte sind auch für Suchmaschinen-Crawler leichter zu indizieren,
00:00:55da die Inhalte bereits beim Laden der Seite verfügbar sind.
00:00:58Dynamisches Rendering hingegen ermöglicht unserer Anwendung,
00:01:02Echtzeit- oder häufig aktualisierte Daten anzuzeigen.
00:01:05Es ermöglicht uns auch,
00:01:06personalisierte Inhalte wie Dashboards und Benutzerprofile bereitzustellen.
00:01:09Zum Beispiel das Vercel-Dashboard.
00:01:12Mit dynamischem Rendering können wir auf Informationen zugreifen,
00:01:15die nur zur Anfragezeit bekannt sein können.
00:01:16In diesem Fall,
00:01:17welcher Benutzer auf sein Dashboard zugreift,
00:01:19nämlich ich.
00:01:20Es gibt bestimmte APIs,
00:01:21die dazu führen können,
00:01:22dass eine Seite dynamisch gerendert wird.
00:01:25Die Verwendung der `params`- und `search params`-Props,
00:01:27die an Seiten oder deren äquivalente Hooks übergeben werden,
00:01:30führt zu dynamischem Rendering.
00:01:32Bei `params` können wir jedoch eine Reihe vorgerenderter Seiten mithilfe generischer statischer `params` vordefinieren und die Seiten auch zwischenspeichern,
00:01:38während sie von Benutzern generiert werden.
00:01:40Darüber hinaus führt das Lesen eingehender Request-Cookies und -Header dazu,
00:01:44dass die Seite dynamisch gerendert wird.
00:01:46Im Gegensatz zu `params` führt der Versuch,
00:01:48etwas mithilfe von Headern oder Cookies zwischenzuspeichern oder vorzurendern,
00:01:51jedoch zu Fehlern während des Builds,
00:01:52da diese Informationen nicht im Voraus bekannt sein können.
00:01:56Schließlich erzwingt die Verwendung von `fetch` mit einer `data cache configuration no store` ebenfalls dynamisches Rendering.
00:02:00Das sind also die wenigen – es gibt noch ein paar weitere APIs,
00:02:03die dynamisches Rendering verursachen können,
00:02:04aber das sind die,
00:02:05denen wir am häufigsten begegnen.
00:02:06In früheren Next-Versionen wurde eine Seite entweder vollständig statisch oder vollständig dynamisch gerendert.
00:02:13Eine einzige dynamische API auf einer Seite führt dazu,
00:02:15dass die gesamte Seite dynamisch gerendert wird.
00:02:17Zum Beispiel eine einfache Authentifizierungsprüfung für den Wert eines Cookies.
00:02:20Durch die Nutzung von React-Serverkomponenten mit Suspense können wir dynamische Inhalte wie ein personalisiertes Willkommensbanner oder Empfehlungen streamen,
00:02:27sobald sie bereit sind,
00:02:29und nur Fallbacks mit Suspense bereitstellen,
00:02:31während statische Inhalte wie ein Newsletter angezeigt werden.
00:02:34Sobald wir jedoch mehrere asynchrone Komponenten auf einer dynamischen Seite hinzufügen,
00:02:38wie z.B.
00:02:39ein Feature-Produkt,
00:02:40würden diese ebenfalls zur Anfragezeit ausgeführt,
00:02:42obwohl sie nicht von dynamischen APIs abhingen..
00:02:45Um also das anfängliche Laden der Seite nicht zu blockieren,
00:02:47würden wir diese Komponenten ebenfalls suspendieren und streamen,
00:02:50was zusätzlichen Aufwand bedeutet,
00:02:52wie das Erstellen von Skeletons und das Sorgen um Dinge wie Layout-Verschiebungen.
00:02:56Seiten sind jedoch oft eine Mischung aus statischen und dynamischen Inhalten.
00:03:01Zum Beispiel eine E-Commerce-App,
00:03:03die von Benutzerinformationen abhängt,
00:03:05aber größtenteils statische Daten enthält.
00:03:07Die erzwungene Wahl zwischen statisch oder dynamisch führt zu viel redundanter Verarbeitung auf dem Server für Inhalte,
00:03:14die sich nie oder sehr selten ändern,
00:03:17und ist nicht optimal für die Leistung.
00:03:19Um dieses Problem zu lösen,
00:03:21wurde auf der letztjährigen Next.js Conf die `use cache`-Direktive angekündigt.
00:03:26Und dieses Jahr,
00:03:27wie wir in der Keynote gesehen haben,
00:03:28ist sie in Next.js 16 verfügbar.
00:03:30Mit `use cache` werden Seiten also nicht mehr gezwungen,
00:03:33entweder statisch oder dynamisch gerendert zu werden.
00:03:36Sie können beides sein.
00:03:37Und Next.js muss nicht mehr raten,
00:03:39was eine Seite ist,
00:03:40basierend darauf,
00:03:41ob sie auf Dinge wie `params` zugreift.
00:03:43Alles ist standardmäßig dynamisch,
00:03:44und `use cache` ermöglicht es uns,
00:03:46explizit Caching zu aktivieren.
00:03:47`use cache` ermöglicht komponierbares Caching.
00:03:51Wir können entweder eine Seite,
00:03:53eine React-Komponente oder eine Funktion als cachebar markieren.
00:03:55Hier können wir die Feature-Produkte-Komponente tatsächlich zwischenspeichern,
00:03:59da sie keine Anfrage und Verarbeitung benötigt und keine dynamischen APIs verwendet.
00:04:03Und diese Cache-Segmente können weiter vorgerendert und als Teil der statischen Shell mit partiellem Pre-Rendering integriert werden,
00:04:09was bedeutet,
00:04:10dass die Feature-Produkte jetzt beim Laden der Seite verfügbar sind und nicht gestreamt werden müssen.
00:04:14Nachdem wir nun dieses wichtige Hintergrundwissen haben,
00:04:18machen wir eine Demo.
00:04:19Eine Verbesserung einer Codebasis mit häufig auftretenden Problemen in Next.js-Apps.
00:04:24Dazu gehören tiefes Prop-Drilling,
00:04:25das die Wartung und Refaktorierung von Features erschwert,
00:04:27redundantes clientseitiges JavaScript und große Komponenten mit mehreren Verantwortlichkeiten sowie das Fehlen von statischem Rendering,
00:04:33was zu zusätzlichen Serverkosten und einer schlechteren Leistung führt.
00:04:36Also, fangen wir an.
00:04:37Und geben Sie mir eine Sekunde hier.
00:04:50Alles klar, super.
00:04:54Das ist also eine sehr einfache Anwendung.
00:04:56Sie ist von einer E-Commerce-Plattform inspiriert.
00:04:59Und lassen Sie mich hier eine erste Demo machen.
00:05:01Ich kann diese Seite also laden.
00:05:03Ich habe Inhalte wie dieses Feature-Produkt.
00:05:06Ich habe Feature-Kategorien, verschiedene Produktdaten.
00:05:09Es gibt auch diese 'Alle durchsuchen'-Seite hier,
00:05:13auf der ich alle Produkte auf der Plattform sehen und zwischen ihnen blättern kann.
00:05:20Dann haben wir hier diese 'Über uns'-Seite,
00:05:22die einfach statisch ist.
00:05:24Ich kann mich auch als Benutzer anmelden.
00:05:27Und das meldet mich bei meinem Benutzer an.
00:05:30Und erhalte dann auch personalisierte Inhalte auf meinem Dashboard hier.
00:05:33Wie zum Beispiel empfohlene Produkte oder diese personalisierten Rabatte hier.
00:05:38Beachten Sie hier, es gibt eine ziemlich gute Mischung.
00:05:42Oh, noch eine Seite, die ich vergessen habe zu zeigen.
00:05:45Die Produktseite, die wichtigste.
00:05:47Auch hier können wir Produktinformationen sehen und sie dann bei Bedarf für unseren Benutzer speichern.
00:05:52Beachten Sie also,
00:05:53dass es in dieser App eine ziemlich gute Mischung aus statischen und dynamischen Inhalten gibt,
00:05:57aufgrund all unserer benutzerabhängigen Funktionen.
00:05:59Werfen wir auch einen Blick auf den Code, der hier wäre.
00:06:05Ich verwende hier natürlich den App Router in Next.js 16.
00:06:08Ich habe alle meine verschiedenen Seiten,
00:06:10wie die 'Über uns'-Seite,
00:06:11die 'Alle'-Seite,
00:06:12unsere Produktseite.
00:06:13Ich habe auch – ich verwende hier Feature Slicing,
00:06:15um meinen App-Ordner sauber zu halten.
00:06:17Ich habe verschiedene Komponenten und Abfragen,
00:06:20die mit meiner Datenbank über Prisma kommunizieren.
00:06:23Also ja, und ich habe das alles absichtlich verlangsamt.
00:06:25Deshalb haben wir diese wirklich lange Ladephase,
00:06:28nur damit wir leichter sehen können,
00:06:30was passiert.
00:06:31Die häufigen Probleme,
00:06:32an denen wir hier arbeiten wollten und die wir tatsächlich in dieser Anwendung haben,
00:06:36waren Prop-Drilling,
00:06:36das die Wartung und Refaktorierung von Features erschwert,
00:06:39übermäßiges clientseitiges JavaScript und das Fehlen von statischem Rendering,
00:06:43was zu zusätzlichen Serverkosten und einer schlechteren Leistung führt.
00:06:47Das Ziel dieser Demo ist es also im Grunde,
00:06:49diese App mit einigen cleveren Mustern bezüglich Komposition,
00:06:53Caching und Architektur zu verbessern,
00:06:55um diese häufigen Probleme zu beheben und sie schneller,
00:06:58skalierbarer und einfacher zu warten.
00:07:01Fangen wir damit an.
00:07:02Das erste Problem,
00:07:03das wir beheben wollen,
00:07:04hängt tatsächlich mit Prop-Drilling zusammen.
00:07:05Und das wäre hier auf der Seite.
00:07:10Beachten Sie hier,
00:07:11ich habe diese `loggedIn`-Variable ganz oben.
00:07:15Und Sie sehen, ich übergebe sie an ein paar Komponenten.
00:07:17Sie wurde tatsächlich über mehrere Ebenen bis in dieses persönliche Banner weitergegeben.
00:07:20Das wird es also schwierig machen,
00:07:22Dinge hier wiederzuverwenden,
00:07:23weil wir immer diese `loggedIn`-Abhängigkeit für unser Willkommensbanner haben.
00:07:28Bei Serverkomponenten wäre es die beste Praxis,
00:07:30das Datenabrufen tatsächlich in die Komponenten zu verschieben,
00:07:34die dies verwenden,
00:07:35und Promises tiefer im Baum aufzulösen.
00:07:37Und damit dies als authentifiziert gilt,
00:07:39solange es entweder `fetch` oder etwas wie React Cache verwendet,
00:07:43können wir mehrere Aufrufe davon duplizieren und es einfach überall in unseren Komponenten wiederverwenden.
00:07:48Das wäre also völlig in Ordnung, es wiederzuverwenden.
00:07:51Jetzt können wir dies also tatsächlich in den personalisierten Bereich hier verschieben.
00:07:54Und wir werden diese Prop nicht mehr benötigen.
00:07:57Und es einfach direkt – hoppla – hier einfügen.
00:08:01Und wir müssen dies nicht mehr übergeben.
00:08:04Und da wir diesen asynchronen Aufruf nun in den personalisierten Bereich verschieben,
00:08:07blockieren wir die Seite nicht mehr.
00:08:09Wir können dies einfach mit einem einfachen Suspense hier suspendieren.
00:08:13Und wir werden diesen Fallback nicht benötigen.
00:08:16Was das Willkommensbanner betrifft,
00:08:19so werden wir wohl dasselbe tun.
00:08:22Aber zu versuchen,
00:08:23die `loggedIn`-Variable oder den Wert hier zu verwenden,
00:08:26funktioniert nicht,
00:08:27oder?
00:08:27Weil dies eine Client-Komponente ist.
00:08:29Wir müssen das also anders lösen.
00:08:30Und wir werden hier ein ziemlich cleveres Muster verwenden,
00:08:33um dies zu lösen.
00:08:33Wir werden tatsächlich ins Layout gehen und hier alles mit einem Auth-Provider umwickeln.
00:08:39Ich werde dies also einfach um meine gesamte App hier legen und diese `loggedIn`-Variable hierher holen.
00:08:45Und ich möchte definitiv nicht mein gesamtes Root-Layout blockieren.
00:08:48Lassen Sie uns hier das `await` entfernen.
00:08:50Und dies einfach als Promise an diesen Auth-Provider weitergeben.
00:08:55Und dies kann einfach diese Promise enthalten.
00:08:57Es kann einfach dort warten,
00:08:59bis wir bereit sind,
00:09:00es zu lesen.
00:09:01Das haben wir jetzt also eingerichtet.
00:09:03Das bedeutet,
00:09:04wir können tatsächlich diese Prop zuerst loswerden.
00:09:09Und wir werden diese,
00:09:10die zum persönlichen Banner durchdringt,
00:09:12loswerden.
00:09:12Und wir werden das Prop-Drilling auch hier oder die Signatur loswerden.
00:09:16Und jetzt können wir diesen Auth-Provider verwenden,
00:09:19um diesen `loggedIn`-Wert lokal innerhalb des persönlichen Banners mit `useAuth` mit dem gerade erstellten Provider abzurufen.
00:09:26Und es mit `use` lesen.
00:09:28Das wird also tatsächlich so funktionieren,
00:09:30dass wir dies suspendieren müssen,
00:09:32während es aufgelöst wird.
00:09:33Ich habe also jetzt diesen kleinen Datenabruf im persönlichen Banner ko-lokalisiert.
00:09:37Und ich muss diese Props nicht mehr herumreichen.
00:09:40Und während dies aufgelöst wird,
00:09:41lassen Sie uns dies auch mit einem Fallback suspendieren.
00:09:44Und lassen Sie uns hier einfach ein allgemeines Banner verwenden,
00:09:48um ungewöhnliche kumulative Verschiebungen zu vermeiden.
00:09:51Und schließlich auch dieses loswerden.
00:09:53Dieses Willkommensbanner ist jetzt also komponierbar.
00:09:58Es ist wiederverwendbar.
00:09:59Wir haben keine seltsamen Props oder Abhängigkeiten auf der Startseite.
00:10:02Und da wir dies so einfach wiederverwenden können,
00:10:05lassen Sie uns es tatsächlich auch zu dieser Browser-Seite hier hinzufügen,
00:10:10die hier sein wird.
00:10:11Und ich kann es einfach hier ohne Abhängigkeiten verwenden.
00:10:15Durch diese Muster können wir eine gute Komponentenarchitektur unter Verwendung von React Cache und React Use aufrechterhalten und unsere Komponenten nutzbarer und komponierbarer machen.
00:10:30Alles klar.
00:10:31Kommen wir zur nächsten häufigen Herausforderung: übermäßiges clientseitiges JavaScript und große Komponenten mit mehreren Verantwortlichkeiten.
00:10:40Tatsächlich ist das auch auf der 'Alle'-Seite hier.
00:10:43Und wieder müssen wir an diesem Willkommensbanner arbeiten.
00:10:46Es ist derzeit eine Client-Komponente.
00:10:48Und der Grund,
00:10:48warum es eine Client-Komponente ist,
00:10:50ist,
00:10:51dass ich hier diesen sehr einfachen 'entlassen'-Zustand habe.
00:10:53Ich kann einfach darauf klicken.
00:10:54Es ist eine schöne UI-Interaktion.
00:10:56Das ist in Ordnung.
00:10:57Was jedoch nicht so gut ist,
00:10:58ist,
00:10:59dass ich diese gesamte Komponente deswegen in eine clientseitige Komponente oder eine Client-Komponente umwandle.
00:11:04Und ich verwende sogar SWR für clientseitiges Abrufen.
00:11:07Ich habe jetzt diese API-Schicht hier.
00:11:08Ich habe keine Typsicherheit mehr in meinen Daten.
00:11:11Ja, das ist nicht notwendig.
00:11:12Und wir verletzen hier auch die Trennung der Belange,
00:11:15weil wir UI-Logik mit Daten vermischen.
00:11:18Also,
00:11:18lassen Sie uns ein weiteres cleveres Muster verwenden,
00:11:20um dies zu beheben.
00:11:21Es wird das Donut-Muster genannt.
00:11:23Im Grunde werde ich dies in einen clientseitigen Wrapper extrahieren.
00:11:27Also, erstellen wir hier eine neue Komponente.
00:11:29Und nennen wir es `BannerContainer`.
00:11:32Und dies wird unsere interaktive Logik mit der `use client`-Direktive enthalten.
00:11:37Wir können die Signatur erstellen.
00:11:38Wir können alles einfügen, was wir gerade hatten.
00:11:42Und anstatt diese Banner zu verwenden,
00:11:44werde ich hier einfach eine Prop einfügen,
00:11:46die die Children sein werden.
00:11:48Deshalb wird es das Donut-Muster genannt.
00:11:50Wir erstellen einfach diese Wrapper-UI-Logik um serverseitig gerenderte Inhalte,
00:11:54oder es könnten serverseitig gerenderte Inhalte sein.
00:11:56Und da wir diese clientseitige Abhängigkeit nicht mehr haben,
00:11:59können wir `use client` entfernen.
00:12:01Wir können stattdessen unsere asynchrone `isAuth`-Funktion hier verwenden.
00:12:06Wir können dies zu einer asynchronen Serverkomponente machen.
00:12:09Wir können sogar clientseitiges Abrufen durch serverseitiges Abrufen ersetzen.
00:12:11Also,
00:12:12lassen Sie mich einfach die Rabattdaten direkt hier abrufen.
00:12:16Rabattdaten.
00:12:18Und einfach unser reguläres mentales Modell wie zuvor mit Typsicherheit nutzen.
00:12:24Und das bedeutet,
00:12:24ich kann auch diese API-Schicht löschen,
00:12:26mit der ich sowieso nicht arbeiten möchte.
00:12:29Schließlich können wir für `isLoading` einfach ein neues Willkommensbanner hier mit unserem Donut-Muster-Banner-Container exportieren,
00:12:35der serverseitig gerenderte Inhalte enthält.
00:12:38Und das bedeutet, wir brauchen `isLoading` nicht mehr.
00:12:40Wir haben also im Grunde das Ganze in eine Serverkomponente refaktorisiert und einen UI-Logikpunkt extrahiert.
00:12:46Aber was ist das?
00:12:48Es sieht so aus, als hätte ich einen weiteren Fehler.
00:12:51Das liegt tatsächlich an Motion.
00:12:53Verwenden Sie Motion.
00:12:54Es ist eine wirklich großartige Animationsbibliothek,
00:12:57aber sie erfordert die `useClient`-Direktive.
00:12:59Und wieder müssen wir dies nicht nur für Animationen zu `useClient` machen.
00:13:03Wir können wieder einen Donut-Muster-Wrapper erstellen und einfach Wrapper für diese Animationen extrahieren.
00:13:10Und das bedeutet,
00:13:11wir müssen hier nichts in clientseitig umwandeln.
00:13:14Und ich vermisse wahrscheinlich etwas hier unten.
00:13:17Ja.
00:13:18Da haben wir's.
00:13:21Jetzt wurde hier also alles auf Server umgestellt.
00:13:23Wir haben die gleiche Interaktion.
00:13:24Wir haben immer noch unsere interaktive Logik hier,
00:13:27aber jetzt haben wir diese eine Möglichkeit,
00:13:28Daten abzurufen.
00:13:29Und wir haben viel weniger clientseitiges JS.
00:13:31Tatsächlich verwende ich dieses Donut-Muster selbst für diesen UI-Boundary-Helfer,
00:13:40der so aussieht.
00:13:42Sehen Sie das?
00:13:43Das zeigt also wieder, was ich meine, oder?
00:13:45Mit dem Donut-Muster haben wir diese Client-Komponente um eine Serverkomponente herum.
00:13:49Ich habe auch viele meiner anderen Komponenten mit diesem UI-Helfer hier markiert.
00:13:53Auch hier habe ich weitere Serverkomponenten.
00:13:56Lassen Sie uns diese auch verbessern,
00:13:58da wir mittlerweile ziemlich gut darin sind.
00:14:01Sie sind im Footer.
00:14:04Diese Kategorien – ich meine,
00:14:05ich habe diese schöne Komponente,
00:14:07die ihre eigenen Daten abruft.
00:14:08Und ich wollte nur diese `showMore`-Funktion hinzufügen,
00:14:11nur für den Fall,
00:14:12dass es wirklich lang wird.
00:14:14Und mit dem Donut-Muster kann ich hier einfach eine `ShowMore`-Komponente umwickeln.
00:14:20Und dies wird meine UI-Logik enthalten.
00:14:23Und es sieht so aus, oder?
00:14:27Ziemlich cool.
00:14:28Und dies enthält nun die Client-Logik,
00:14:30die es uns ermöglicht,
00:14:31State zu verwenden.
00:14:33Wir verwenden die `children count` und `toArray`,
00:14:35um dies zu schneiden.
00:14:36Und das Coole hier ist,
00:14:37dass diese beiden jetzt vollständig komponierbare,
00:14:39wiederverwendbare Komponenten sind,
00:14:41die so zusammenarbeiten.
00:14:42Das ist also wirklich die Schönheit dieser Muster,
00:14:44die wir hier lernen.
00:14:45Sie können dies für alles verwenden.
00:14:50Ich verwende es auch für dieses Modal hier.
00:14:52Ja,
00:14:52denken Sie einfach daran,
00:14:54wenn Sie das nächste Mal erwägen,
00:14:55irgendeine Art von Client-Logik zu Ihren Serverkomponenten hinzuzufügen.
00:14:59Okay, wir kennen das Donut-Muster.
00:15:01Wir wissen,
00:15:01wie man es nutzt,
00:15:02um diese komponierbaren Komponenten zu erstellen und clientseitiges JS zu vermeiden,
00:15:07sodass wir zum letzten Problem übergehen können.
00:15:10Lassen Sie mich das noch einmal schließen.
00:15:13Das wäre also der Mangel an statischen Rendering-Strategien,
00:15:18richtig?
00:15:18Wenn ich mir meine Build-Ausgabe ansehe,
00:15:20habe ich tatsächlich jede einzelne Seite als dynamische Seite hier.
00:15:24Das bedeutet also,
00:15:25dass jedes Mal,
00:15:25wenn ich hier etwas lade,
00:15:26dies für jeden einzelnen Benutzer ausgeführt wird.
00:15:29Entschuldigung.
00:15:30Jeder einzelne Benutzer,
00:15:31der dies öffnet,
00:15:31wird diesen Ladezustand erhalten.
00:15:33Das wird Serverkosten verschwenden und die Leistung verschlechtern.
00:15:37Und das bedeutet auch,
00:15:38dass etwas in meinen Seiten dynamisches Rendering verursacht oder dynamisches Rendering für alle meine Seiten erzwingt.
00:15:45Tatsächlich ist es in meinem Root-Layout.
00:15:49Ich weiß nicht, ob Sie das erlebt haben.
00:15:51Es ist hier drüben.
00:15:53In meinem Header habe ich dieses Benutzerprofil.
00:15:57Und dies verwendet natürlich Cookies,
00:15:58um den aktuellen Benutzer abzurufen,
00:16:00und das bedeutet,
00:16:00dass alles andere auch dynamisch gerendert wird.
00:16:02Denn noch einmal,
00:16:03Seiten können entweder dynamisch oder statisch sein,
00:16:06richtig?
00:16:06Dies ist ein ziemlich häufiges Problem und etwas,
00:16:09das in früheren Next-Versionen bereits gelöst wurde,
00:16:11also schauen wir mal,
00:16:12was wir tun könnten.
00:16:13Eine Sache,
00:16:14die wir tun könnten,
00:16:15ist,
00:16:15eine Routengruppe zu erstellen und unsere App in statische und dynamische Abschnitte aufzuteilen,
00:16:20was es mir ermöglichen würde,
00:16:21meine 'Über uns'-Seite zu extrahieren.
00:16:23Ich könnte dies statisch rendern.
00:16:25Für einige Apps ist das in Ordnung,
00:16:27aber in meinem Fall ist die wichtige Seite die Produktseite,
00:16:30und diese ist immer noch dynamisch,
00:16:31also nicht wirklich hilfreich.
00:16:33Wie wäre es mit dieser Strategie?
00:16:35Hier erstelle ich also diesen Request-Kontext-Parameter,
00:16:38der einen bestimmten Zustand in meine URL kodiert,
00:16:40und dann kann ich `generate static params` verwenden,
00:16:43um alle verschiedenen Varianten meiner Seiten zu generieren.
00:16:46Das würde tatsächlich,
00:16:47kombiniert mit clientseitigem Abrufen der Benutzerdaten,
00:16:50es mir ermöglichen,
00:16:51dies auf meiner Produktseite zwischenzuspeichern.
00:16:54Definitiv ein praktikables Muster.
00:16:55Es wird vom Vercel Flags SDK empfohlen,
00:16:58ich glaube,
00:16:58es heißt Precompute-Muster.
00:17:00Aber das ist wirklich komplex,
00:17:01und ich habe mehrere Möglichkeiten,
00:17:03Daten abzurufen.
00:17:04Und eigentlich möchte ich meine ganze App nicht darauf umschreiben.
00:17:07Was wäre also,
00:17:07wenn wir keine dieser Workarounds machen müssten?
00:17:10Was wäre, wenn es einen einfacheren Weg gäbe?
00:17:12Nun, den gibt es.
00:17:14Kehren wir zurück zu unserer Anwendung.
00:17:17Wir können also tatsächlich zur Next-Konfiguration gehen und einfach Cache-Komponenten aktivieren.
00:17:23Oh, schön.
00:17:25Okay,
00:17:25und was dies tut,
00:17:26wie Sie aus der Keynote wissen,
00:17:28wird tatsächlich alle unsere asynchronen Aufrufe zur Anfragezeit oder dynamisch machen.
00:17:34Und es wird uns auch Fehler geben,
00:17:36wann immer wir einen asynchronen Aufruf nicht suspendiert haben,
00:17:39und es wird uns diese `use cache`-Direktive geben,
00:17:42die wir verwenden können,
00:17:43um granular entweder eine Seite,
00:17:45eine Funktion oder eine Komponente zwischenzuspeichern.
00:17:48Also ja, lassen Sie uns dies nutzen.
00:17:51Wir können hier mit der Startseite beginnen.
00:17:55Werfen wir einen Blick darauf.
00:17:56Also wieder habe ich diese Mischung aus statischen und dynamischen Inhalten.
00:17:59Ich habe mein Willkommensbanner für mich,
00:18:01etwas für Sie,
00:18:02auch für mich.
00:18:03Schauen wir uns das noch einmal mit diesem UI-Helfer an.
00:18:06Zum Beispiel wird das Banner hier dynamisch gerendert.
00:18:10Während ich dies als Hybrid-Rendering markiert habe,
00:18:14weil der Hero dieses asynchrone Ding abruft und es ziemlich langsam ist.
00:18:18Aber es hängt nicht von Benutzerdaten oder dynamischen APIs ab.
00:18:21Das bedeutet also,
00:18:22dass alles,
00:18:23was hier hybrid gerendert wird,
00:18:24tatsächlich über Anfragen und Benutzer hinweg wiederverwendet werden kann.
00:18:27Und wir können die `use cache`-Direktive dafür verwenden.
00:18:30Also,
00:18:30fügen wir hier die `use cache`-Direktive hinzu und markieren dies als gecacht.
00:18:35Und das wird es mir ermöglichen – wann immer ich diese Seite neu lade – ich habe das nicht gespeichert.
00:18:43Da haben wir's.
00:18:44Dieser Teil wird nicht neu geladen, weil er gecacht ist.
00:18:47Es ist jetzt statisch, richtig?
00:18:49Und es gibt auch andere verwandte APIs wie den Cache-Tag,
00:18:53der es mir ermöglicht,
00:18:54dies zu typisieren oder den spezifischen Cache-Eintrag granular zu validieren oder meine Offenbarungsperiode zu definieren.
00:19:01Aber für diese Demo konzentrieren wir uns einfach auf die einfache Direktive.
00:19:05Nachdem ich nun diese `use cache`-Direktive habe,
00:19:07kann ich tatsächlich meine Suspense-Grenze um diesen Hero entfernen.
00:19:10Und das bedeutet – nun,
00:19:12was dies bewirken wird,
00:19:13ist,
00:19:14dass partielles Pre-Rendering dies tatsächlich in die statisch vorgerenderte Shell aufnehmen kann,
00:19:19sodass dieser Hero in diesem Fall ein Teil meiner Build-Ausgabe sein wird.
00:19:23Machen wir dasselbe für alles andere auf dieser Seite,
00:19:27das geteilt werden kann.
00:19:28Zum Beispiel habe ich hier diese Feature-Kategorien.
00:19:31Lassen Sie uns dort dasselbe tun.
00:19:33Und die `use cache`-Direktive hinzufügen und dies als gecacht markieren.
00:19:37So.
00:19:39Und wir können die Suspense-Grenze entfernen.
00:19:40Wir werden dies nicht mehr benötigen.
00:19:43Dasselbe gilt für die Feature-Produkte.
00:19:44Fügen wir `use cache` hinzu und markieren dies als gecacht.
00:19:48Hoppla.
00:19:50Und dann die Suspense-Grenze entfernen.
00:19:52Beachten Sie also,
00:19:53wie viel Komplexität ich hier einfach entfernen kann.
00:19:55Ich muss mir keine Sorgen mehr um meine Skeletons,
00:19:57meine kumulative Layout-Verschiebung machen,
00:19:59die ich zuvor hatte.
00:20:00Und die Seite ist nicht mehr – oder wir haben diese seitenweite statische versus dynamische Einschränkung nicht mehr.
00:20:07Wenn ich diese Seite jetzt lade,
00:20:10sehen Sie,
00:20:10dass hier alles gecacht ist,
00:20:12außer diesem wirklich benutzerspezifischen Inhalt.
00:20:16Richtig.
00:20:18Das ist also ziemlich cool.
00:20:19Gehen wir zur 'Durchsuchen'-Seite und machen dort dasselbe.
00:20:24Ja.
00:20:25Ich habe hier bereits alle meine Grenzen markiert,
00:20:27damit Sie leicht verstehen können,
00:20:28was passiert.
00:20:29Und ich möchte zumindest diese Kategorien zwischenspeichern.
00:20:33Es sieht jedoch so aus, als würde ich einen Fehler bekommen.
00:20:37Vielleicht erkennen Sie das.
00:20:38Das bedeutet also, ich habe eine blockierende Route.
00:20:40Und ich verwende keine Suspense-Grenze,
00:20:42obwohl ich es sollte.
00:20:43Das Aktualisieren zeigt, es stimmt, oder?
00:20:46Das ist wirklich langsam.
00:20:47Und es verursacht Leistungsprobleme und eine schlechte UX.
00:20:50Das ist also großartig.
00:20:51`use cache` oder Cache-Komponenten helfen mir,
00:20:53meine blockierenden Routen zu identifizieren.
00:20:55Schauen wir uns tatsächlich an, was darin passiert.
00:20:57Das ist also das Problem, richtig?
00:20:59Ich rufe diese Kategorien auf oberster Ebene ab und habe keine Suspense-Grenze darüber.
00:21:03Im Grunde müssen wir eine Wahl treffen.
00:21:05Entweder wir fügen eine Suspense-Grenze darüber hinzu oder wir entscheiden uns für Caching.
00:21:09Machen wir zuerst das Einfache und fügen hier einfach eine `loading.tsx` hinzu.
00:21:12Und fügen wir hier eine Ladeseite hinzu,
00:21:17eine schöne Skeleton-UI.
00:21:21Das ist ziemlich gut.
00:21:21Es hat den Fehler behoben,
00:21:22aber es passiert nichts Nützliches auf dieser Seite,
00:21:25während ich warte.
00:21:25Ich kann nicht einmal suchen.
00:21:27Mit Cache-Komponenten ist dynamisch wie – oder statisch versus dynamisch ist wie eine Skala.
00:21:33Und es liegt an uns zu entscheiden,
00:21:35wie viel Statik wir in unseren Seiten haben wollen.
00:21:37Also,
00:21:37verschieben wir diese Seite mehr in Richtung statisch und löschen diese `loading.tsx` wieder.
00:21:43Und dann nutzen wir die Muster,
00:21:44die wir zuvor gelernt haben,
00:21:46um diesen Datenabruf in die Komponente zu verschieben und ihn mit der UI zu ko-lokalisieren.
00:21:50Verschieben Sie dies also hier in meine responsiven Kategoriefilter.
00:21:54Ich habe zwei wegen responsivem Design.
00:21:57Ich kann es tatsächlich einfach hier hinzufügen.
00:22:01Hoppla.
00:22:03Und dies importieren.
00:22:05Ich brauche diese Prop nicht mehr.
00:22:06Tatsächlich wird meine Komponente komponierbarer.
00:22:09Und anstatt es zu suspendieren,
00:22:11fügen wir einfach die `use cache`-Direktive hinzu.
00:22:14Und das sollte genügen.
00:22:16Beachten Sie also,
00:22:17wie ich gezwungen werde,
00:22:18mehr darüber nachzudenken,
00:22:19wo ich meine Promises auflöse und dadurch tatsächlich meine Komponentenarchitektur verbessere.
00:22:24Ich muss dies nicht suspendieren.
00:22:25Dies wird einfach hier in der statischen Shell enthalten sein.
00:22:28Die Produktliste,
00:22:30lassen Sie mich diese einfach frisch halten.
00:22:35Damit ich das jedes Mal neu laden kann.
00:22:37Während die Kategorien unten,
00:22:39diese möchte ich auch zwischenspeichern.
00:22:41Also, gehen wir zum Footer.
00:22:44Und da ich hier das Donut-Muster verwende,
00:22:47kann dies tatsächlich zwischengespeichert werden,
00:22:50obwohl es sich in diesem interaktiven Teil der UI befindet.
00:22:54Das ist also völlig in Ordnung.
00:22:55Dieses Muster war also nicht nur gut für die Komposition,
00:22:57sondern auch für das Caching.
00:22:58Ich glaube, ich habe dort noch einen Fehler.
00:23:03Mal sehen, was das ist.
00:23:04Habe immer noch diesen Fehler.
00:23:08Das liegt tatsächlich an diesen Suchparametern.
00:23:10Suchparameter, wie wir wissen, sind eine dynamische API.
00:23:12Ich kann das nicht zwischenspeichern.
00:23:13Aber ich kann es tiefer auflösen,
00:23:15um mehr von meiner UI freizulegen und sie statisch zu machen.
00:23:18Also,
00:23:18lassen Sie uns dies nach unten verschieben,
00:23:21es als Promise an die Produktliste übergeben.
00:23:24Wir werden dies hier als Promise typisieren, so.
00:23:30Lassen Sie uns es in der Produktliste auflösen,
00:23:33die aufgelösten Suchparameter hier und hier verwenden.
00:23:36Und da dies hier suspendiert ist,
00:23:38wird der Fehler verschwunden sein.
00:23:40Wenn ich dies also neu lade,
00:23:42ist das Einzige,
00:23:43was hier neu geladen wird,
00:23:44nur der Teil,
00:23:45den ich speziell als dynamisch ausgewählt habe.
00:23:48Alles andere kann zwischengespeichert werden.
00:23:49Und das bedeutet,
00:23:50dass ich mit meinem Banner interagieren oder sogar suchen kann,
00:23:54weil dieser Teil bereits vorgerendert wurde.
00:23:57Alles klar,
00:23:58machen wir die letzte Seite hier,
00:24:01die Produktseite,
00:24:02die schwierigste und wichtigste.
00:24:05Es ist im Moment wirklich schlecht.
00:24:08Das ist anscheinend super wichtig für eine E-Commerce-Plattform.
00:24:11Alles klar, beheben wir das auch.
00:24:15Hier habe ich also diese Produktseite.
00:24:18Beginnen wir damit,
00:24:19nur die wiederverwendbaren Inhalte hier zwischenzuspeichern,
00:24:22zum Beispiel das Produkt selbst.
00:24:23Und fügen Sie hier einfach `use cache` hinzu und markieren Sie dies als gecacht.
00:24:27Das sollte in Ordnung sein.
00:24:28Das bedeutet, wir können die Suspense-Grenze hier entfernen.
00:24:33Alles klar,
00:24:33und dies wird hier nicht mehr bei jeder Anfrage neu geladen,
00:24:37richtig?
00:24:38Für die Produktdetails machen wir dasselbe.
00:24:40Fügen wir `use cache` hinzu.
00:24:41Markieren wir es als gecacht und sehen,
00:24:44ob das auch funktioniert.
00:24:47Das hat nicht funktioniert.
00:24:48Tatsächlich ist dies ein anderer Fehler.
00:24:50Es sagt mir,
00:24:50dass ich versuche,
00:24:51dynamische APIs innerhalb dieses gecachten Segments zu verwenden.
00:24:54Und das stimmt.
00:24:54Ich verwende den 'Produkt speichern'-Button, richtig?
00:24:56Das ermöglichte es mir,
00:24:57den gespeicherten Zustand anzuklicken und umzuschalten.
00:25:00Was denken Sie, können wir damit tun?
00:25:03Wir können das Donut-Muster wieder verwenden.
00:25:06Tatsächlich können wir auch dynamische Segmente in Cache-Segmente einfügen.
00:25:10Wir verschachteln sie also wie zuvor, aber mit Cache.
00:25:12Das ist also ziemlich cool.
00:25:14Lassen Sie uns hier die Children so hinzufügen.
00:25:19Und dies wird den Fehler beheben.
00:25:21Und ich kann dies einfach um dieses eine dynamische Segment meiner Seite hier wickeln,
00:25:26die Suspense-Grenze entfernen und nur eine sehr kleine Lesezeichen-UI für dieses eine dynamische Stück der Seite hinzufügen.
00:25:34Und schauen wir mal, wie das jetzt aussieht.
00:25:40Beachten Sie also,
00:25:40wie fast die gesamte UI verfügbar ist,
00:25:42aber ich habe diesen einen kleinen Teil,
00:25:45der dynamisch ist,
00:25:45und das ist in Ordnung.
00:25:47Alles andere ist noch da.
00:25:48Und lassen wir die Bewertungen dynamisch,
00:25:51weil wir sie frisch halten könnten.
00:25:53Es gibt immer noch einen weiteren Fehler.
00:25:54Lassen Sie uns das schnell angehen.
00:25:56Wieder sind dies die `params`.
00:25:58Ich bekomme Hilfe,
00:25:59dass ich eine Wahl treffen muss,
00:26:00entweder einen Lade-Fallback hinzuzufügen oder dies zwischenzuspeichern.
00:26:04Verwenden wir in diesem Fall einfach `generate static params`.
00:26:07Hängt irgendwie von Ihrem Anwendungsfall und Ihrem Datensatz ab.
00:26:10Aber für diesen Fall werde ich einfach ein paar vorgerenderte,
00:26:12vordefinierte Seiten hinzufügen und den Rest dann zwischenspeichern,
00:26:15sobald sie von Benutzern generiert werden.
00:26:17Und dies wird meinen Fehler hier beheben.
00:26:20Ich denke,
00:26:21ich bin tatsächlich mit meinem Refactoring fertig.
00:26:22Lassen Sie uns die bereitgestellte Version ansehen und sehen,
00:26:25wie das aussieht.
00:26:26Ich habe dies also gerade auf Vercel bereitgestellt.
00:26:27Und denken Sie daran,
00:26:30ich habe hier absichtlich viele Datenabrufe verlangsamt.
00:26:35Und trotzdem,
00:26:36wenn ich diese Seite anfänglich lade,
00:26:38ist alles bereits verfügbar.
00:26:40Das Einzige hier sind nur diese wenigen dynamischen Segmente wie die Rabatte und die 'für dich'-Inhalte.
00:26:46Dasselbe gilt für 'Alle durchsuchen'.
00:26:47Die gesamte UI ist bereits verfügbar.
00:26:50Und für das Produkt selbst fühlt es sich einfach sofort an.
00:26:54Und denken Sie noch einmal daran,
00:26:56dass all diese Cache-Segmente mit der statischen Shell durch partielles Pre-Rendering enthalten sein werden.
00:27:00Und es kann mit dem verbesserten Prefetching im neuen Next 16 Client Router vorab abgerufen werden.
00:27:05Das bedeutet also,
00:27:06dass jede Navigation einfach – es fühlt sich einfach so schnell an,
00:27:09oder?
00:27:09Alles klar,
00:27:10zusammenfassend lässt sich sagen: Mit Cache-Komponenten gibt es kein statisch versus dynamisch mehr.
00:27:17Und wir müssen dynamische APIs nicht vermeiden oder dynamische Inhalte kompromittieren.
00:27:28Und wir können diese komplexen Hacks und Workarounds mit mehreren Datenabrufstrategien überspringen,
00:27:33nur für diesen einen – diesen Cache-Hit,
00:27:36wie ich Ihnen gezeigt habe.
00:27:37Im modernen Next.js ist dynamisch versus statisch eine Skala.
00:27:40Und wir entscheiden,
00:27:41wie viel Statik wir in unseren Apps haben wollen.
00:27:43Und solange wir bestimmte Muster befolgen,
00:27:45können wir ein mentales Modell haben,
00:27:47das standardmäßig performant,
00:27:49komponierbar und skalierbar ist.
00:27:50Also, kehren wir zu den Folien zurück.
00:27:53Wenn Sie also nicht – wir nicht schon von der Geschwindigkeit beeindruckt sind,
00:27:55hier ist der Lighthouse-Score.
00:27:56Ich habe also einige Felddaten mit den Vercel Speed Insights gesammelt.
00:28:00Wir haben also einen Score von 100 auf allen wichtigsten Seiten,
00:28:03der Startseite,
00:28:04der Produktseite und der Produktliste,
00:28:06obwohl sie hochdynamisch sind.
00:28:08Fassen wir also abschließend die Muster zusammen,
00:28:10die Skalierbarkeit und Leistung in Next.js-Apps gewährleisten und es uns ermöglichen,
00:28:15die neuesten Innovationen zu nutzen und solche Scores zu erzielen.
00:28:18Erstens können wir unsere Architektur verfeinern,
00:28:21indem wir Promises tief im Komponentenbaum auflösen und Daten lokal innerhalb von Komponenten mit React Cache abrufen,
00:28:26um doppelte Arbeit zu vermeiden.
00:28:28Wir können übermäßiges Prop-Passing an Client-Komponenten vermeiden,
00:28:31indem wir Kontext-Provider in Kombination mit React Use verwenden.
00:28:35Zweitens können wir Server- und Client-Komponenten mithilfe des Donut-Musters zusammensetzen,
00:28:38um clientseitiges JavaScript zu reduzieren,
00:28:40eine klare Trennung der Belange zu gewährleisten und die Wiederverwendung von Komponenten zu ermöglichen.
00:28:43Und dieses Muster wird es uns später ermöglichen,
00:28:46unsere zusammengesetzten Serverkomponenten zwischenzuspeichern.
00:28:50Und schließlich können wir mit `use cache` entweder nach Seite,
00:28:52Komponente oder Funktion cachen und vorrendern,
00:28:55um redundante Verarbeitung zu eliminieren,
00:28:56Leistung und SEO zu steigern und partielles Pre-Rendering diese Segmente der App statisch rendern zu lassen.
00:29:01Und wenn unsere Inhalte wirklich dynamisch sind,
00:29:04können wir sie mit geeigneten Lade-Fallbacks suspendieren.
00:29:07Und denken Sie daran,
00:29:07dass all dies miteinander verbunden ist.
00:29:09Je besser Ihre Architektur ist,
00:29:10desto einfacher ist es,
00:29:10sie zu komponieren,
00:29:11und desto einfacher wird es sein,
00:29:12sie mit den besten Ergebnissen zwischenzuspeichern und vorzurendern.
00:29:15Zum Beispiel ermöglicht das Auflösen dynamischer APIs tief im Baum die Erstellung einer größeren,
00:29:19teilweise vorgerenderten statischen Shell.
00:29:22Und damit ist dies das Repo der fertigen Version der Anwendung.
00:29:25Es gibt so viele Dinge,
00:29:26die ich dort nicht einmal gezeigt habe,
00:29:28die Sie sich ansehen können.
00:29:29Und Sie können den QR-Code scannen,
00:29:31um meine sozialen Medien dort neben dem Repo zu finden,
00:29:33wenn Sie kein Bild machen und es selbst eingeben möchten.
00:29:36Also ja, das war's von mir.
00:29:37Vielen Dank an die Next.js Conf, dass ich hier sein durfte.
00:29:39[MUSIK SPIELT]