Menu

Migrationsanleitung für den App Router

Diese Anleitung hilft Ihnen:

Upgrade

Node.js-Version

Die minimale Node.js-Version ist jetzt v18.17. Weitere Informationen finden Sie in der Node.js-Dokumentation.

Next.js-Version

Um auf Next.js Version 13 zu aktualisieren, führen Sie folgenden Befehl mit Ihrem bevorzugten Paketmanager aus:

Terminal
npm install next@latest react@latest react-dom@latest

ESLint-Version

Wenn Sie ESLint verwenden, müssen Sie Ihre ESLint-Version upgraden:

Terminal
npm install -D eslint-config-next@latest

Hinweis: Möglicherweise müssen Sie den ESLint-Server in VS Code neu starten, damit die Änderungen wirksam werden. Öffnen Sie die Befehlspalette (cmd+shift+p auf Mac; ctrl+shift+p auf Windows) und suchen Sie nach ESLint: Restart ESLint Server.

Nächste Schritte

Nach dem Update sehen Sie sich die folgenden Abschnitte für die nächsten Schritte an:

Neue Features upgraden

Next.js 13 führte den neuen App Router mit neuen Features und Konventionen ein. Der neue Router ist im app-Verzeichnis verfügbar und koexistiert mit dem pages-Verzeichnis.

Das Upgrade auf Next.js 13 erfordert nicht die Nutzung des neuen App Routers. Sie können pages weiterhin mit neuen Features verwenden, die in beiden Verzeichnissen funktionieren, wie die aktualisierte Bild-Komponente, Link-Komponente, Script-Komponente und Schriftartenoptimierung.

<Image/>-Komponente

Next.js 12 führte Verbesserungen an der Bild-Komponente mit einer temporären Import: next/future/image ein. Diese Verbesserungen umfassten weniger Client-seitiges JavaScript, einfachere Möglichkeiten zum Erweitern und Gestalten von Bildern, bessere Barrierefreiheit und natives Browser-Lazy-Loading.

In Version 13 ist dieses neue Verhalten jetzt der Standard für next/image.

Es gibt zwei Codemods, um Ihnen bei der Migration zur neuen Bild-Komponente zu helfen:

  • next-image-to-legacy-image Codemod: Benennt next/image-Importe sicher und automatisch zu next/legacy/image um. Bestehende Komponenten behalten das gleiche Verhalten bei.
  • next-image-experimental Codemod: Fügt gefährlich Inline-Stile hinzu und entfernt ungenutzte Props. Dies ändert das Verhalten bestehender Komponenten, um den neuen Standardeinstellungen zu entsprechen. Um diesen Codemod zu verwenden, müssen Sie zuerst den next-image-to-legacy-image-Codemod ausführen.

Die <Link>-Komponente erfordert nicht mehr das manuelle Hinzufügen eines <a>-Tags als untergeordnetes Element. Dieses Verhalten wurde in Version 12.2 als experimentelle Option hinzugefügt und ist jetzt der Standard. In Next.js 13 rendert <Link> immer <a> und ermöglicht Ihnen, Props an das zugrunde liegende Tag weiterzuleiten.

Beispiel:

import Link from 'next/link'
 
// Next.js 12: `<a>` muss verschachtelt sein, sonst wird es ausgeschlossen
<Link href="/about">
  <a>About</a>
</Link>
 
// Next.js 13: `<Link>` rendert `<a>` immer im Hintergrund
<Link href="/about">
  About
</Link>

Um Ihre Links auf Next.js 13 zu aktualisieren, können Sie den new-link Codemod verwenden.

<Script>-Komponente

Das Verhalten von next/script wurde aktualisiert, um sowohl pages als auch app zu unterstützen, aber einige Änderungen müssen vorgenommen werden, um eine reibungslose Migration sicherzustellen:

  • Verschieben Sie alle beforeInteractive-Skripte, die Sie zuvor in _document.js eingefügt haben, in die Root-Layout-Datei (app/layout.tsx).
  • Die experimentelle worker-Strategie funktioniert noch nicht in app, und Skripte, die mit dieser Strategie gekennzeichnet sind, müssen entweder entfernt oder so geändert werden, dass sie eine andere Strategie verwenden (z. B. lazyOnload).
  • onLoad-, onReady- und onError-Handler funktionieren nicht in Server-Komponenten, stellen Sie also sicher, dass Sie sie in eine Client-Komponente verschieben oder vollständig entfernen.

Schriftartenoptimierung

Bisher half Ihnen Next.js bei der Optimierung von Schriftarten durch Inline-Setzen von Schriftarten-CSS. Version 13 führt das neue next/font-Modul ein, das Ihnen die Möglichkeit gibt, Ihre Schriftartenladevorgänge anzupassen und gleichzeitig eine hervorragende Leistung und Privatsphäre zu gewährleisten. next/font wird in beiden Verzeichnissen, pages und app, unterstützt.

Während Inline-CSS in pages weiterhin funktioniert, funktioniert es nicht in app. Sie sollten stattdessen next/font verwenden.

Weitere Informationen zur Verwendung von next/font finden Sie auf der Seite Schriftartenoptimierung.

Migration von pages zu app

🎥 Anschauen: Lernen Sie, wie Sie den App Router schrittweise einführen → YouTube (16 Minuten).

Der Wechsel zum App Router ist möglicherweise das erste Mal, dass Sie React-Features verwenden, auf denen Next.js aufbaut, wie Server-Komponenten, Suspense und mehr. In Kombination mit neuen Next.js-Features wie spezielle Dateien und Layouts bedeutet die Migration das Erlernen neuer Konzepte, Denkmodelle und Verhaltensänderungen.

Wir empfehlen, die kombinierte Komplexität dieser Updates zu reduzieren, indem Sie Ihre Migration in kleinere Schritte unterteilen. Das app-Verzeichnis ist absichtlich so gestaltet, dass es gleichzeitig mit dem pages-Verzeichnis funktioniert, um eine schrittweise Seitenmigrationen zu ermöglichen.

  • Das app-Verzeichnis unterstützt verschachtelte Routen und Layouts. Mehr erfahren.
  • Verwenden Sie verschachtelte Ordner zum Definieren von Routen und eine spezielle page.js-Datei, um ein Routensegment öffentlich zugänglich zu machen. Mehr erfahren.
  • Spezielle Dateikonventionen werden verwendet, um die Benutzeroberfläche für jedes Routensegment zu erstellen. Die häufigsten speziellen Dateien sind page.js und layout.js.
    • Verwenden Sie page.js, um eine für eine Route einzigartige Benutzeroberfläche zu definieren.
    • Verwenden Sie layout.js, um eine Benutzeroberfläche zu definieren, die über mehrere Routen hinweg geteilt wird.
    • .js, .jsx oder .tsx Dateierweiterungen können für spezielle Dateien verwendet werden.
  • Sie können andere Dateien wie Komponenten, Stile, Tests und mehr im app-Verzeichnis zusammen ablegen. Mehr erfahren.
  • Datenabruffunktionen wie getServerSideProps und getStaticProps wurden durch eine neue API im app-Verzeichnis ersetzt. getStaticPaths wurde durch generateStaticParams ersetzt.
  • pages/_app.js und pages/_document.js wurden durch ein einzelnes app/layout.js Root-Layout ersetzt. Mehr erfahren.
  • pages/_error.js wurde durch granularere error.js-Spezialdateien ersetzt. Mehr erfahren.
  • pages/404.js wurde durch die not-found.js-Datei ersetzt.
  • pages/api/* API-Routen wurden durch die route.js (Route Handler) Spezialdatei ersetzt.

Schritt 1: Erstellen des app-Verzeichnisses

Aktualisieren Sie auf die neueste Next.js-Version (erfordert 13.4 oder höher):

npm install next@latest

Erstellen Sie dann ein neues app-Verzeichnis im Stammverzeichnis Ihres Projekts (oder im src/-Verzeichnis).

Schritt 2: Erstellen eines Root-Layouts

Erstellen Sie eine neue app/layout.tsx-Datei im app-Verzeichnis. Dies ist ein Root-Layout, das für alle Routen im app-Verzeichnis gilt.

app/layout.tsx
TypeScript
export default function RootLayout({
  // Layouts müssen eine children-Eigenschaft akzeptieren.
  // Dies wird mit verschachtelten Layouts oder Seiten gefüllt werden
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
  • Das app-Verzeichnis muss ein Root-Layout enthalten.
  • Das Root-Layout muss <html>- und <body>-Tags definieren, da Next.js diese nicht automatisch erstellt
  • Das Root-Layout ersetzt die pages/_app.tsx- und pages/_document.tsx-Dateien.
  • .js, .jsx oder .tsx Erweiterungen können für Layout-Dateien verwendet werden.

Um <head>-HTML-Elemente zu verwalten, können Sie die integrierte SEO-Unterstützung verwenden:

app/layout.tsx
TypeScript
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'Home',
  description: 'Welcome to Next.js',
}

Migrieren von _document.js und _app.js

Wenn Sie eine bestehende _app- oder _document-Datei haben, können Sie die Inhalte (z.B. globale Stile) in das Root-Layout (app/layout.tsx) kopieren. Stile in app/layout.tsx werden nicht auf pages/* angewendet. Sie sollten _app/_document während der Migration beibehalten, um zu verhindern, dass Ihre pages/*-Routen unterbrochen werden. Sobald die Migration vollständig ist, können Sie sie sicher löschen.

Wenn Sie React-Kontextanbieter verwenden, müssen diese in eine Client-Komponente verschoben werden.

Migrieren des getLayout()-Musters zu Layouts (optional)

Next.js empfahl das Hinzufügen einer Eigenschaft zu Seitenkomponenten im pages-Verzeichnis, um seitenspezifische Layouts zu erreichen. Dieses Muster kann durch native Unterstützung für verschachtelte Layouts im app-Verzeichnis ersetzt werden.

Beispiel vor und nach der Änderung

Vorher

components/DashboardLayout.js
export default function DashboardLayout({ children }) {
  return (
    <div>
      <h2>My Dashboard</h2>
      {children}
    </div>
  )
}
pages/dashboard/index.js
import DashboardLayout from '../components/DashboardLayout'
 
export default function Page() {
  return <p>My Page</p>
}
 
Page.getLayout = function getLayout(page) {
  return <DashboardLayout>{page}</DashboardLayout>
}

Nachher

  • Entfernen Sie die Page.getLayout-Eigenschaft aus pages/dashboard/index.js und folgen Sie den Schritten zum Migrieren von Seiten in das app-Verzeichnis.

    app/dashboard/page.js
    export default function Page() {
      return <p>My Page</p>
    }
  • Verschieben Sie den Inhalt von DashboardLayout in eine neue Client-Komponente, um das Verhalten des pages-Verzeichnisses beizubehalten.

    app/dashboard/DashboardLayout.js
    'use client' // Diese Direktive sollte am Anfang der Datei stehen, vor allen Importen.
     
    // Dies ist eine Client-Komponente
    export default function DashboardLayout({ children }) {
      return (
        <div>
          <h2>My Dashboard</h2>
          {children}
        </div>
      )
    }
  • Importieren Sie das DashboardLayout in eine neue layout.js-Datei im app-Verzeichnis.

    app/dashboard/layout.js
    import DashboardLayout from './DashboardLayout'
     
    // Dies ist eine Server-Komponente
    export default function Layout({ children }) {
      return <DashboardLayout>{children}</DashboardLayout>
    }
  • Sie können nicht-interaktive Teile von DashboardLayout.js (Client-Komponente) schrittweise in layout.js (Server-Komponente) verschieben, um die Menge an Komponenten-JavaScript zu reduzieren, die an den Client gesendet wird.

Schritt 3: Migrieren von next/head

Im pages-Verzeichnis wird die next/head React-Komponente verwendet, um <head>-HTML-Elemente wie title und meta zu verwalten. Im app-Verzeichnis wird next/head durch die neue integrierte SEO-Unterstützung ersetzt.

Vorher:

pages/index.tsx
TypeScript
import Head from 'next/head'
 
export default function Page() {
  return (
    <>
      <Head>
        <title>My page title</title>
      </Head>
    </>
  )
}

Nachher:

app/page.tsx
TypeScript
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'My Page Title',
}
 
export default function Page() {
  return '...'
}

Alle Metadaten-Optionen ansehen.

Schritt 4: Migrieren von Seiten

  • Seiten im app-Verzeichnis sind standardmäßig Server-Komponenten. Dies unterscheidet sich vom pages-Verzeichnis, in dem Seiten Client-Komponenten sind.
  • Datenabruf hat sich im app-Verzeichnis geändert. getServerSideProps, getStaticProps und getInitialProps wurden durch eine einfachere API ersetzt.
  • Das app-Verzeichnis verwendet verschachtelte Ordner zum Definieren von Routen und eine spezielle page.js-Datei, um ein Routensegment öffentlich zugänglich zu machen.
  • pages-Verzeichnisapp-VerzeichnisRoute
    index.jspage.js/
    about.jsabout/page.js/about
    blog/[slug].jsblog/[slug]/page.js/blog/post-1

Wir empfehlen, die Migration einer Seite in zwei Hauptschritten durchzuführen:

  • Schritt 1: Verschieben Sie die standardmäßig exportierte Seitenkomponente in eine neue Client-Komponente.
  • Schritt 2: Importieren Sie die neue Client-Komponente in eine neue page.js-Datei im app-Verzeichnis.

Hinweis: Dies ist der einfachste Migrationspfad, da er das vergleichbarste Verhalten zum pages-Verzeichnis aufweist.

Schritt 1: Erstellen einer neuen Client-Komponente

  • Erstellen Sie eine neue separate Datei im app-Verzeichnis (z.B. app/home-page.tsx oder ähnlich), die eine Client-Komponente exportiert. Um Client-Komponenten zu definieren, fügen Sie die 'use client'-Anweisung am Anfang der Datei hinzu (vor allen Importen).
    • Ähnlich wie im Pages Router gibt es einen Optimierungsschritt, um Client-Komponenten beim ersten Seitenaufruf zu statischem HTML vorzurendern.
  • Verschieben Sie die standardmäßig exportierte Seitenkomponente von pages/index.js nach app/home-page.tsx.
app/home-page.tsx
TypeScript
'use client'
 
// Dies ist eine Client-Komponente (identisch mit Komponenten im `pages`-Verzeichnis)
// Sie empfängt Daten als Props, hat Zugriff auf Status und Effekte und wird
// während des ersten Seitenladens auf dem Server vorgerendert.
export default function HomePage({ recentPosts }) {
  return (
    <div>
      {recentPosts.map((post) => (
        <div key={post.id}>{post.title}</div>
      ))}
    </div>
  )
}

Schritt 2: Erstellen einer neuen Seite

  • Erstellen Sie eine neue app/page.tsx-Datei im app-Verzeichnis. Dies ist standardmäßig eine Server-Komponente.

  • Importieren Sie die home-page.tsx-Client-Komponente in die Seite.

  • Wenn Sie Daten in pages/index.js abgerufen haben, verschieben Sie die Datenabruf-Logik direkt in die Server-Komponente mithilfe der neuen Datenabruf-APIs. Weitere Details finden Sie im Datenabruf-Upgrade-Leitfaden.

    app/page.tsx
    TypeScript
    // Importieren Sie Ihre Client-Komponente
    import HomePage from './home-page'
     
    async function getPosts() {
      const res = await fetch('https://...')
      const posts = await res.json()
      return posts
    }
     
    export default async function Page() {
      // Daten direkt in einer Server-Komponente abrufen
      const recentPosts = await getPosts()
      // Abgerufene Daten an Ihre Client-Komponente weiterleiten
      return <HomePage recentPosts={recentPosts} />
    }
  • Wenn Ihre vorherige Seite useRouter verwendet hat, müssen Sie auf die neuen Routing-Hooks umstellen. Weitere Informationen.

  • Starten Sie Ihren Entwicklungsserver und besuchen Sie http://localhost:3000. Sie sollten Ihre bestehende Index-Route sehen, die jetzt über das App-Verzeichnis bereitgestellt wird.

Schritt 5: Migration von Routing-Hooks

Ein neuer Router wurde hinzugefügt, um das neue Verhalten im app-Verzeichnis zu unterstützen.

In app sollten Sie die drei neuen Hooks aus next/navigation verwenden: useRouter(), usePathname() und useSearchParams().

  • Der neue useRouter-Hook wird aus next/navigation importiert und hat ein anderes Verhalten als der useRouter-Hook in pages, der aus next/router importiert wird.
  • Der neue useRouter gibt den pathname-String nicht zurück. Verwenden Sie stattdessen den separaten usePathname-Hook.
  • Der neue useRouter gibt das query-Objekt nicht zurück. Suchparameter und dynamische Routenparameter sind jetzt getrennt. Verwenden Sie stattdessen die useSearchParams- und useParams-Hooks.
  • Sie können useSearchParams und usePathname zusammen verwenden, um Seitenwechsel zu überwachen. Weitere Details finden Sie im Abschnitt Router-Ereignisse.
  • Diese neuen Hooks werden nur in Client-Komponenten unterstützt. Sie können nicht in Server-Komponenten verwendet werden.
app/example-client-component.tsx
TypeScript
'use client'
 
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
 
export default function ExampleClientComponent() {
  const router = useRouter()
  const pathname = usePathname()
  const searchParams = useSearchParams()
 
  // ...
}

Zusätzlich hat der neue useRouter-Hook folgende Änderungen:

  • isFallback wurde entfernt, da fallback ersetzt wurde.
  • Die Werte locale, locales, defaultLocales, domainLocales wurden entfernt, da integrierte i18n-Funktionen von Next.js im app-Verzeichnis nicht mehr erforderlich sind. Weitere Informationen zu i18n.
  • basePath wurde entfernt. Die Alternative wird nicht Teil von useRouter sein. Es wurde noch nicht implementiert.
  • asPath wurde entfernt, da das Konzept von as aus dem neuen Router entfernt wurde.
  • isReady wurde entfernt, da es nicht mehr erforderlich ist. Während des statischen Renderings überspringt jede Komponente, die den useSearchParams()-Hook verwendet, den Vorrendering-Schritt und wird stattdessen zur Laufzeit auf dem Client gerendert.
  • route wurde entfernt. usePathname oder useSelectedLayoutSegments() bieten eine Alternative.

API-Referenz für useRouter() anzeigen.

Komponenten zwischen pages und app teilen

Um Komponenten zwischen den pages und app Routern kompatibel zu halten, verwenden Sie den useRouter Hook aus next/compat/router. Dies ist der useRouter Hook aus dem pages-Verzeichnis, der jedoch dazu gedacht ist, Komponenten zwischen Routern zu teilen. Sobald Sie bereit sind, ihn nur im app-Router zu verwenden, aktualisieren Sie auf den neuen useRouter aus next/navigation.

Schritt 6: Migration von Daten-Abrufmethoden

Das pages-Verzeichnis verwendet getServerSideProps und getStaticProps, um Daten für Seiten abzurufen. Im app-Verzeichnis werden diese vorherigen Daten-Abruffunktionen durch eine einfachere API ersetzt, die auf fetch() und async React Server Components aufbaut.

app/page.tsx
TypeScript
export default async function Page() {
  // Diese Anfrage sollte zwischengespeichert werden, bis sie manuell für ungültig erklärt wird.
  // Ähnlich wie `getStaticProps`.
  // `force-cache` ist standardmäßig eingestellt und kann weggelassen werden.
  const staticData = await fetch(`https://...`, { cache: 'force-cache' })
 
  // Diese Anfrage sollte bei jeder Anfrage neu abgerufen werden.
  // Ähnlich wie `getServerSideProps`.
  const dynamicData = await fetch(`https://...`, { cache: 'no-store' })
 
  // Diese Anfrage sollte mit einer Lebensdauer von 10 Sekunden zwischengespeichert werden.
  // Ähnlich wie `getStaticProps` mit der `revalidate`-Option.
  const revalidatedData = await fetch(`https://...`, {
    next: { revalidate: 10 },
  })
 
  return <div>...</div>
}

Server-Side Rendering (getServerSideProps)

Im pages-Verzeichnis wird getServerSideProps verwendet, um Daten auf dem Server abzurufen und Props an die standardmäßig exportierte React-Komponente in der Datei weiterzuleiten. Die ursprüngliche HTML-Seite wird vom Server vorgerendert, gefolgt vom "Hydratisieren" der Seite im Browser (interaktiv machen).

pages/dashboard.js
// `pages`-Verzeichnis
 
export async function getServerSideProps() {
  const res = await fetch(`https://...`)
  const projects = await res.json()
 
  return { props: { projects } }
}
 
export default function Dashboard({ projects }) {
  return (
    <ul>
      {projects.map((project) => (
        <li key={project.id}>{project.name}</li>
      ))}
    </ul>
  )
}

Im App Router können wir unseren Daten-Abruf innerhalb unserer React-Komponenten mit Server Components zusammenführen. Dies ermöglicht es uns, weniger JavaScript an den Client zu senden und gleichzeitig die gerenderte HTML vom Server beizubehalten.

Indem wir die cache-Option auf no-store setzen, können wir angeben, dass die abgerufenen Daten niemals zwischengespeichert werden sollen. Dies ähnelt getServerSideProps im pages-Verzeichnis.

app/dashboard/page.tsx
TypeScript
// `app`-Verzeichnis
 
// Diese Funktion kann beliebig benannt werden
async function getProjects() {
  const res = await fetch(`https://...`, { cache: 'no-store' })
  const projects = await res.json()
 
  return projects
}
 
export default async function Dashboard() {
  const projects = await getProjects()
 
  return (
    <ul>
      {projects.map((project) => (
        <li key={project.id}>{project.name}</li>
      ))}
    </ul>
  )
}

Zugriff auf das Request-Objekt

Im pages-Verzeichnis können Sie anfragenbezogene Daten basierend auf der Node.js HTTP-API abrufen.

Beispielsweise können Sie das req-Objekt aus getServerSideProps abrufen und es verwenden, um die Cookies und Header der Anfrage abzurufen.

pages/index.js
// `pages`-Verzeichnis
 
export async function getServerSideProps({ req, query }) {
  const authHeader = req.getHeaders()['authorization'];
  const theme = req.cookies['theme'];
 
  return { props: { ... }}
}
 
export default function Page(props) {
  return ...
}

Das app-Verzeichnis stellt neue schreibgeschützte Funktionen bereit, um Anfragedaten abzurufen:

  • headers: Basierend auf der Web Headers API und kann innerhalb von Server Components verwendet werden, um Anfrage-Header abzurufen.
  • cookies: Basierend auf der Web Cookies API und kann innerhalb von Server Components verwendet werden, um Cookies abzurufen.
app/page.tsx
TypeScript
// `app`-Verzeichnis
import { cookies, headers } from 'next/headers'
 
async function getData() {
  const authHeader = (await headers()).get('authorization')
 
  return '...'
}
 
export default async function Page() {
  // Sie können `cookies` oder `headers` innerhalb von Server Components
  // direkt oder in Ihrer Daten-Abruffunktion verwenden
  const theme = (await cookies()).get('theme')
  const data = await getData()
  return '...'
}

Statische Seitengenerierung (getStaticProps)

Im pages-Verzeichnis wird die getStaticProps-Funktion verwendet, um eine Seite zur Bauzeit vorher zu rendern. Diese Funktion kann verwendet werden, um Daten von einer externen API oder direkt aus einer Datenbank abzurufen und diese Daten während der Erstellung an die gesamte Seite weiterzugeben.

pages/index.js
// `pages`-Verzeichnis
 
export async function getStaticProps() {
  const res = await fetch(`https://...`)
  const projects = await res.json()
 
  return { props: { projects } }
}
 
export default function Index({ projects }) {
  return projects.map((project) => <div>{project.name}</div>)
}

Im app-Verzeichnis wird der Daten-Abruf mit fetch() standardmäßig auf cache: 'force-cache' gesetzt, was die Anfragedaten so lange zwischenspeichert, bis sie manuell für ungültig erklärt werden. Dies ähnelt getStaticProps im pages-Verzeichnis.

app/page.js
// `app`-Verzeichnis
 
// Diese Funktion kann beliebig benannt werden
async function getProjects() {
  const res = await fetch(`https://...`)
  const projects = await res.json()
 
  return projects
}
 
export default async function Index() {
  const projects = await getProjects()
 
  return projects.map((project) => <div>{project.name}</div>)
}

Dynamische Pfade (getStaticPaths)

Im pages-Verzeichnis wird die getStaticPaths-Funktion verwendet, um die dynamischen Pfade zu definieren, die zur Bauzeit vorher gerendert werden sollen.

pages/posts/[id].js
// `pages`-Verzeichnis
import PostLayout from '@/components/post-layout'
 
export async function getStaticPaths() {
  return {
    paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
  }
}
 
export async function getStaticProps({ params }) {
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()
 
  return { props: { post } }
}
 
export default function Post({ post }) {
  return <PostLayout post={post} />
}

Im app-Verzeichnis wird getStaticPaths durch generateStaticParams ersetzt.

generateStaticParams verhält sich ähnlich wie getStaticPaths, hat jedoch eine vereinfachte API zum Zurückgeben von Routenparametern und kann in Layouts verwendet werden. Die Rückgabestruktur von generateStaticParams ist ein Array von Segmenten anstelle eines Arrays von verschachtelten param-Objekten oder einer Zeichenfolge von aufgelösten Pfaden.

app/posts/[id]/page.js
// `app`-Verzeichnis
import PostLayout from '@/components/post-layout'
 
export async function generateStaticParams() {
  return [{ id: '1' }, { id: '2' }]
}
 
async function getPost(params) {
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()
 
  return post
}
 
export default async function Post({ params }) {
  const post = await getPost(params)
 
  return <PostLayout post={post} />
}

Die Verwendung des Namens generateStaticParams ist für das neue Modell im app-Verzeichnis passender als getStaticPaths. Das Präfix get wird durch das beschreibendere generate ersetzt, was jetzt besser allein funktioniert, da getStaticProps und getServerSideProps nicht mehr erforderlich sind. Das Suffix Paths wird durch Params ersetzt, was für verschachtelte Routing mit mehreren dynamischen Segmenten angemessener ist.

Ersetzen von fallback

Im pages-Verzeichnis wird die fallback-Eigenschaft, die von getStaticPaths zurückgegeben wird, verwendet, um das Verhalten einer Seite zu definieren, die zur Bauzeit nicht vorgerendert wurde. Diese Eigenschaft kann auf true gesetzt werden, um eine Fallback-Seite während der Seitengenerierung anzuzeigen, auf false, um eine 404-Seite anzuzeigen, oder auf blocking, um die Seite zum Anfragezeitpunkt zu generieren.

pages/posts/[id].js
// `pages`-Verzeichnis
 
export async function getStaticPaths() {
  return {
    paths: [],
    fallback: 'blocking'
  };
}
 
export async function getStaticProps({ params }) {
  ...
}
 
export default function Post({ post }) {
  return ...
}

Im app-Verzeichnis steuert die Eigenschaft config.dynamicParams, wie Parameter außerhalb von generateStaticParams behandelt werden:

  • true: (Standard) Dynamische Segmente, die nicht in generateStaticParams enthalten sind, werden bei Bedarf generiert.
  • false: Dynamische Segmente, die nicht in generateStaticParams enthalten sind, geben einen 404-Fehler zurück.

Dies ersetzt die Option fallback: true | false | 'blocking' von getStaticPaths im pages-Verzeichnis. Die Option fallback: 'blocking' ist in dynamicParams nicht enthalten, da der Unterschied zwischen 'blocking' und true beim Streaming vernachlässigbar ist.

app/posts/[id]/page.js
// `app`-Verzeichnis
 
export const dynamicParams = true;
 
export async function generateStaticParams() {
  return [...]
}
 
async function getPost(params) {
  ...
}
 
export default async function Post({ params }) {
  const post = await getPost(params);
 
  return ...
}

Bei dynamicParams, das auf true (Standard) gesetzt ist, wird bei einer Anfrage eines Routensegments, das nicht generiert wurde, dieses serverseitig gerendert und zwischengespeichert.

Inkrementelle statische Regenerierung (getStaticProps mit revalidate)

Im pages-Verzeichnis ermöglicht die Funktion getStaticProps das Hinzufügen eines revalidate-Feldes, um eine Seite nach einer bestimmten Zeit automatisch neu zu generieren.

pages/index.js
// `pages`-Verzeichnis
 
export async function getStaticProps() {
  const res = await fetch(`https://.../posts`)
  const posts = await res.json()
 
  return {
    props: { posts },
    revalidate: 60,
  }
}
 
export default function Index({ posts }) {
  return (
    <Layout>
      <PostList posts={posts} />
    </Layout>
  )
}

Im app-Verzeichnis kann Datenabruf mit fetch() revalidate verwenden, was die Anfrage für die angegebene Anzahl von Sekunden zwischenspeichert.

app/page.js
// `app`-Verzeichnis
 
async function getPosts() {
  const res = await fetch(`https://.../posts`, { next: { revalidate: 60 } })
  const data = await res.json()
 
  return data.posts
}
 
export default async function PostList() {
  const posts = await getPosts()
 
  return posts.map((post) => <div>{post.name}</div>)
}

API-Routen

API-Routen funktionieren weiterhin im Verzeichnis pages/api ohne Änderungen. Sie wurden jedoch im app-Verzeichnis durch Route-Handler ersetzt.

Route-Handler ermöglichen das Erstellen benutzerdefinierter Anforderungshandler für eine bestimmte Route mithilfe der Web Request- und Response-APIs.

app/api/route.ts
export async function GET(request: Request) {}
app/api/route.js
export async function GET(request) {}

Hinweis: Wenn Sie zuvor API-Routen verwendet haben, um eine externe API vom Client aus aufzurufen, können Sie jetzt stattdessen Server-Komponenten verwenden, um Daten sicher abzurufen. Weitere Informationen zum Datenabruf.

Schritt 7: Styling

Im pages-Verzeichnis sind globale Stylesheets auf pages/_app.js beschränkt. Mit dem app-Verzeichnis wurde diese Einschränkung aufgehoben. Globale Stile können zu jedem Layout, jeder Seite oder Komponente hinzugefügt werden.

Tailwind CSS

Wenn Sie Tailwind CSS verwenden, müssen Sie das app-Verzeichnis zu Ihrer tailwind.config.js-Datei hinzufügen:

tailwind.config.js
module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx,mdx}', // <-- Diese Zeile hinzufügen
    './pages/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
  ],
}

Sie müssen auch Ihre globalen Stile in Ihre app/layout.js-Datei importieren:

app/layout.js
import '../styles/globals.css'
 
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

Weitere Informationen zum Styling mit Tailwind CSS

Codemods

Next.js bietet Codemod-Transformationen, um das Upgrade Ihrer Codebasis zu unterstützen, wenn ein Feature veraltet ist. Weitere Informationen finden Sie unter Codemods.