Von Pages zu App
Dieser Leitfaden hilft Ihnen dabei:
- Ihre Next.js-Anwendung von Version 12 auf Version 13 zu aktualisieren
- Funktionen zu aktualisieren, die sowohl im
pages
- als auch imapp
-Verzeichnis funktionieren - Ihre bestehende Anwendung schrittweise von
pages
zuapp
zu migrieren
Aktualisierung
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 den folgenden Befehl mit Ihrem bevorzugten Paketmanager aus:
npm install next@latest react@latest react-dom@latest
ESLint-Version
Falls Sie ESLint verwenden, müssen Sie Ihre ESLint-Version aktualisieren:
npm install -D eslint-config-next@latest
Gut zu wissen: Möglicherweise müssen Sie den ESLint-Server in VS Code neu starten, damit die ESLint-Änderungen wirksam werden. Öffnen Sie die Befehlspalette (
cmd+shift+p
auf Mac;ctrl+shift+p
auf Windows) und suchen Sie nachESLint: Restart ESLint Server
.
Nächste Schritte
Nach der Aktualisierung finden Sie in den folgenden Abschnitten die nächsten Schritte:
- Neue Funktionen aktualisieren: Ein Leitfaden, der Ihnen hilft, neue Funktionen wie die verbesserten Image- und Link-Komponenten zu nutzen.
- Migration vom
pages
- zumapp
-Verzeichnis: Eine Schritt-für-Schritt-Anleitung, die Ihnen hilft, schrittweise vonpages
zuapp
zu migrieren.
Neue Funktionen aktualisieren
Next.js 13 hat den neuen App-Router mit neuen Funktionen und Konventionen eingeführt. Der neue Router ist im app
-Verzeichnis verfügbar und koexistiert mit dem pages
-Verzeichnis.
Die Aktualisierung auf Next.js 13 erfordert nicht die Verwendung des neuen App-Routers. Sie können weiterhin pages
mit neuen Funktionen verwenden, die in beiden Verzeichnissen funktionieren, wie die aktualisierte Image-Komponente, Link-Komponente, Script-Komponente und Schriftoptimierung.
<Image/>
-Komponente
Next.js 12 hat Verbesserungen an der Image-Komponente mit einem temporären Import eingeführt: next/future/image
. Diese Verbesserungen umfassten weniger clientseitiges JavaScript, einfachere Möglichkeiten zur Erweiterung und Gestaltung 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, die Ihnen bei der Migration zur neuen Image-Komponente helfen:
next-image-to-legacy-image
Codemod: Benenntnext/image
-Importe sicher und automatisch innext/legacy/image
um. Bestehende Komponenten behalten das gleiche Verhalten bei.next-image-experimental
Codemod: Fügt gefährlich Inline-Styles hinzu und entfernt ungenutzte Props. Dies ändert das Verhalten bestehender Komponenten, um den neuen Standard zu entsprechen. Um diesen Codemod zu verwenden, müssen Sie zuerst dennext-image-to-legacy-image
-Codemod ausführen.
<Link>
-Komponente
Die <Link>
-Komponente erfordert nicht mehr manuell das Hinzufügen eines <a>
-Tags als Kind. Dieses Verhalten wurde als experimentelle Option in Version 12.2 hinzugefügt und ist jetzt der Standard. In Next.js 13 rendert <Link>
immer <a>
und ermöglicht es 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 immer `<a>` 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 sind erforderlich, um eine reibungslose Migration zu gewährleisten:
- Verschieben Sie alle
beforeInteractive
-Scripts, die Sie zuvor in_document.js
eingebunden haben, in die Root-Layout-Datei (app/layout.tsx
). - Die experimentelle
worker
-Strategie funktioniert noch nicht inapp
, und Scripts mit dieser Strategie müssen entweder entfernt oder so geändert werden, dass sie eine andere Strategie verwenden (z.B.lazyOnload
). onLoad
,onReady
undonError
-Handler funktionieren nicht in Server-Komponenten, daher sollten Sie sie in eine Client-Komponente verschieben oder ganz entfernen.
Schriftoptimierung
Bisher half Next.js Ihnen, Schriften zu optimieren, indem Schrift-CSS inline eingebettet wurde. Version 13 führt das neue next/font
-Modul ein, das Ihnen die Möglichkeit gibt, Ihre Schriftladeerfahrung anzupassen und gleichzeitig eine hervorragende Leistung und Privatsphäre zu gewährleisten. next/font
wird sowohl im pages
- als auch im app
-Verzeichnis 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 Schriftoptimierung.
Migration von pages
zu app
🎥 Ansehen: Erfahren Sie, wie Sie den App-Router schrittweise einführen → YouTube (16 Minuten).
Der Wechsel zum App-Router kann das erste Mal sein, dass Sie React-Funktionen verwenden, auf denen Next.js aufbaut, wie Server-Komponenten, Suspense und mehr. In Kombination mit neuen Next.js-Funktionen wie Spezialdateien und Layouts bedeutet die Migration neue Konzepte, mentale Modelle und Verhaltensänderungen, die es zu lernen gilt.
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 Migration Seite für Seite zu ermöglichen.
- Das
app
-Verzeichnis unterstützt verschachtelte Routen und Layouts. Mehr erfahren. - Verwenden Sie verschachtelte Ordner, um Routen zu definieren, 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 Spezialdateien sind
page.js
undlayout.js
.- Verwenden Sie
page.js
, um eine Benutzeroberfläche zu definieren, die für eine Route einzigartig ist. - Verwenden Sie
layout.js
, um eine Benutzeroberfläche zu definieren, die über mehrere Routen hinweg geteilt wird. - Die Dateierweiterungen
.js
,.jsx
oder.tsx
können für Spezialdateien verwendet werden.
- Verwenden Sie
- Sie können andere Dateien wie Komponenten, Styles, Tests und mehr im
app
-Verzeichnis zusammenstellen. Mehr erfahren. - Datenabruffunktionen wie
getServerSideProps
undgetStaticProps
wurden durch eine neue API inapp
ersetzt.getStaticPaths
wurde durchgenerateStaticParams
ersetzt. pages/_app.js
undpages/_document.js
wurden durch ein einzelnesapp/layout.js
-Root-Layout ersetzt. Mehr erfahren.pages/_error.js
wurde durch granularereerror.js
-Spezialdateien ersetzt. Mehr erfahren.pages/404.js
wurde durch dienot-found.js
-Datei ersetzt.pages/api/*
-API-Routen wurden durch dieroute.js
-Spezialdatei (Route-Handler) 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 innerhalb von app
gilt.
export default function RootLayout({
// Layouts müssen eine children-Prop akzeptieren.
// Diese wird mit verschachtelten Layouts oder Seiten gefüllt
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
export default function RootLayout({
// Layouts müssen eine children-Prop akzeptieren.
// Diese wird mit verschachtelten Layouts oder Seiten gefüllt
children,
}) {
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 Dateien
pages/_app.tsx
undpages/_document.tsx
. - Die Dateierweiterungen
.js
,.jsx
oder.tsx
können für Layout-Dateien verwendet werden.
Um <head>
-HTML-Elemente zu verwalten, können Sie die integrierte SEO-Unterstützung verwenden:
import { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Home',
description: 'Welcome to Next.js',
}
export const metadata = {
title: 'Home',
description: 'Welcome to Next.js',
}
Migration von _document.js
und _app.js
Falls Sie eine bestehende _app
- oder _document
-Datei haben, können Sie deren Inhalte (z.B. globale Styles) in das Root-Layout (app/layout.tsx
) kopieren. Styles in app/layout.tsx
gelten nicht für pages/*
. Sie sollten _app
/_document
während der Migration behalten, um zu verhindern, dass Ihre pages/*
-Routen brechen. Nach vollständiger Migration können Sie sie dann sicher löschen.
Falls Sie React Context-Provider verwenden, müssen diese in eine Client-Komponente verschoben werden.
Migration des getLayout()
-Musters zu Layouts (Optional)
Next.js empfahl, eine Eigenschaft zu Page-Komponenten hinzuzufügen, um pro-Seite-Layouts im pages
-Verzeichnis zu erreichen. Dieses Muster kann durch die native Unterstützung für verschachtelte Layouts im app
-Verzeichnis ersetzt werden.
Vorher- und Nachher-Beispiel ansehen
Vorher
export default function DashboardLayout({ children }) {
return (
<div>
<h2>My Dashboard</h2>
{children}
</div>
)
}
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 auspages/dashboard/index.js
und folgen Sie den Schritten zur Migration von Seiten in dasapp
-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 despages
-Verzeichnisses beizubehalten.app/dashboard/DashboardLayout.js 'use client' // Diese Direktive sollte am Anfang der Datei stehen, vor allen Imports. // Dies ist eine Client-Komponente export default function DashboardLayout({ children }) { return ( <div> <h2>My Dashboard</h2> {children} </div> ) }
-
Importieren Sie
DashboardLayout
in eine neuelayout.js
-Datei imapp
-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 inlayout.js
(Server-Komponente) verschieben, um die Menge an Komponenten-JavaScript, die Sie an den Client senden, zu reduzieren.
Schritt 3: Migration 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:
import Head from 'next/head'
export default function Page() {
return (
<>
<Head>
<title>My page title</title>
</Head>
</>
)
}
import Head from 'next/head'
export default function Page() {
return (
<>
<Head>
<title>My page title</title>
</Head>
</>
)
}
Nachher:
import { Metadata } from 'next'
export const metadata: Metadata = {
title: 'My Page Title',
}
export default function Page() {
return '...'
}
export const metadata = {
title: 'My Page Title',
}
export default function Page() {
return '...'
}
Alle Metadaten-Optionen ansehen.
Schritt 4: Migration von Seiten
- Seiten im
app
-Verzeichnis sind standardmäßig Server Components (Server-Komponenten). Dies unterscheidet sich vompages
-Verzeichnis, wo Seiten Client Components (Client-Komponenten) sind. - Data Fetching (Datenabruf) hat sich in
app
geändert.getServerSideProps
,getStaticProps
undgetInitialProps
wurden durch eine einfachere API ersetzt. - Das
app
-Verzeichnis verwendet verschachtelte Ordner, um Routen zu definieren, und eine speziellepage.js
-Datei, um ein Routensegment öffentlich zugänglich zu machen. -
pages
-Verzeichnisapp
-VerzeichnisRoute index.js
page.js
/
about.js
about/page.js
/about
blog/[slug].js
blog/[slug]/page.js
/blog/post-1
Wir empfehlen, die Migration einer Seite in zwei Hauptschritte aufzuteilen:
- Schritt 1: Die standardmäßig exportierte Page-Komponente in eine neue Client-Komponente verschieben.
- Schritt 2: Die neue Client-Komponente in eine neue
page.js
-Datei imapp
-Verzeichnis importieren.
Gut zu wissen: 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'
-Direktive an den Anfang der Datei (vor allen Imports) hinzu.- Ähnlich wie beim Pages Router gibt es einen Optimierungsschritt, um Client-Komponenten beim ersten Seitenaufruf als statisches HTML vorzurrendern.
- Verschieben Sie die standardmäßig exportierte Page-Komponente von
pages/index.js
nachapp/home-page.tsx
.
'use client'
// Dies ist eine Client-Komponente (wie Komponenten im `pages`-Verzeichnis)
// Sie empfängt Daten als Props, hat Zugriff auf State und Effekte und wird
// beim ersten Seitenaufruf auf dem Server vorgerendert.
export default function HomePage({ recentPosts }) {
return (
<div>
{recentPosts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
)
}
'use client'
// Dies ist eine Client-Komponente. Sie empfängt Daten als Props und
// hat Zugriff auf State und Effekte, genau wie Page-Komponenten
// im `pages`-Verzeichnis.
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 imapp
-Verzeichnis. Dies ist standardmäßig eine Server-Komponente. -
Importieren Sie die
home-page.tsx
-Client-Komponente in die Seite. -
Falls Sie in
pages/index.js
Daten abgerufen haben, verschieben Sie die Datenabruflogik direkt in die Server-Komponente unter Verwendung der neuen Data Fetching APIs (Datenabruf-APIs). Weitere Details finden Sie im Leitfaden zur Datenabrufmigration.// 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} /> }
// 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} /> }
-
Falls Ihre vorherige Seite
useRouter
verwendet hat, müssen Sie auf die neuen Routing-Hooks umstellen. Mehr erfahren. -
Starten Sie Ihren Entwicklungsserver und rufen Sie
http://localhost:3000
auf. Sie sollten Ihre bestehende Index-Route sehen, die nun über dasapp
-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 verwenden, die aus next/navigation
importiert werden: useRouter()
, usePathname()
und useSearchParams()
.
- Der neue
useRouter
-Hook wird ausnext/navigation
importiert und verhält sich anders als deruseRouter
-Hook inpages
, der ausnext/router
importiert wird.- Der
useRouter
-Hook ausnext/router
wird imapp
-Verzeichnis nicht unterstützt, kann aber weiterhin impages
-Verzeichnis verwendet werden.
- Der
- Der neue
useRouter
gibt keinepathname
-Zeichenkette zurück. Verwenden Sie stattdessen den separatenusePathname
-Hook. - Der neue
useRouter
gibt keinquery
-Objekt zurück. Verwenden Sie stattdessen den separatenuseSearchParams
-Hook. - Sie können
useSearchParams
undusePathname
zusammen verwenden, um Seitenänderungen zu verfolgen. Weitere Details finden Sie im Abschnitt Router Events (Router-Ereignisse). - Diese neuen Hooks werden nur in Client-Komponenten unterstützt. Sie können nicht in Server-Komponenten verwendet werden.
'use client'
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
export default function ExampleClientComponent() {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
// ...
}
'use client'
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
export default function ExampleClientComponent() {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
// ...
}
Darüber hinaus weist der neue useRouter
-Hook folgende Änderungen auf:
isFallback
wurde entfernt, dafallback
ersetzt wurde.- Die Werte
locale
,locales
,defaultLocales
,domainLocales
wurden entfernt, da die integrierten i18n-Funktionen von Next.js imapp
-Verzeichnis nicht mehr notwendig sind. Mehr über i18n erfahren. basePath
wurde entfernt. Die Alternative wird nicht Teil vonuseRouter
sein. Sie wurde noch nicht implementiert.asPath
wurde entfernt, da das Konzept vonas
aus dem neuen Router entfernt wurde.isReady
wurde entfernt, da es nicht mehr notwendig ist. Während des Static Rendering (statischen Renderings) wird jede Komponente, die denuseSearchParams()
-Hook verwendet, den Prerendering-Schritt überspringen und stattdessen zur Laufzeit auf dem Client gerendert.
Siehe die useRouter()
-API-Referenz.
Schritt 6: Migration von Data Fetching-Methoden
Das pages
-Verzeichnis verwendet getServerSideProps
und getStaticProps
, um Daten für Seiten abzurufen. Im app
-Verzeichnis wurden diese bisherigen Data Fetching-Funktionen durch eine einfachere API ersetzt, die auf fetch()
und async
React Server Components basiert.
export default async function Page() {
// Diese Anfrage sollte zwischengespeichert werden, bis sie manuell ungültig gemacht wird.
// Ähnlich wie `getStaticProps`.
// `force-cache` ist der Standardwert 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>
}
export default async function Page() {
// Diese Anfrage sollte zwischengespeichert werden, bis sie manuell ungültig gemacht wird.
// Ähnlich wie `getStaticProps`.
// `force-cache` ist der Standardwert 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-seitiges 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 weiterzugeben. Das initiale HTML für die Seite wird vom Server vorgerendert, gefolgt vom "Hydratisieren" der Seite im Browser (um sie interaktiv zu machen).
// `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
-Verzeichnis können wir unseren Datenabruf direkt in unseren React-Komponenten mit Server Components (Server-Komponenten) platzieren. Dies ermöglicht es uns, weniger JavaScript an den Client zu senden, während das gerenderte HTML vom Server beibehalten wird.
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`-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>
)
}
// `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 anforderungsbasierte Daten basierend auf der Node.js HTTP-API abrufen.
Beispielsweise können Sie das req
-Objekt aus getServerSideProps
abrufen und verwenden, um die Cookies und Header der Anfrage zu erhalten.
// `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 zum Abrufen von Anforderungsdaten bereit:
headers()
: Basierend auf der Web Headers API und kann in Server Components (Server-Komponenten) verwendet werden, um Anforderungsheader abzurufen.cookies()
: Basierend auf der Web Cookies API und kann in Server Components (Server-Komponenten) verwendet werden, um Cookies abzurufen.
// `app`-Verzeichnis
import { cookies, headers } from 'next/headers'
async function getData() {
const authHeader = headers().get('authorization')
return '...'
}
export default async function Page() {
// Sie können `cookies()` oder `headers()` direkt in Server-Komponenten
// oder in Ihrer Data Fetching-Funktion verwenden
const theme = cookies().get('theme')
const data = await getData()
return '...'
}
// `app`-Verzeichnis
import { cookies, headers } from 'next/headers'
async function getData() {
const authHeader = headers().get('authorization')
return '...'
}
export default async function Page() {
// Sie können `cookies()` oder `headers()` direkt in Server-Komponenten
// oder in Ihrer Data Fetching-Funktion verwenden
const theme = cookies().get('theme')
const data = await getData()
return '...'
}
Static Site Generation (getStaticProps
)
Im pages
-Verzeichnis wird die Funktion getStaticProps
verwendet, um eine Seite zum Build-Zeitpunkt vorzurrendern. Diese Funktion kann verwendet werden, um Daten von einer externen API oder direkt aus einer Datenbank abzurufen und diese Daten während des Builds an die gesamte Seite weiterzugeben.
// `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 Datenabruf mit fetch()
standardmäßig auf cache: 'force-cache'
gesetzt, was die Anforderungsdaten bis zur manuellen Ungültigmachung zwischenspeichert. Dies ähnelt getStaticProps
im pages
-Verzeichnis.
// `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 Funktion getStaticPaths
verwendet, um die dynamischen Pfade zu definieren, die zur Build-Zeit vorgerendert werden sollen.
// `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
, bietet jedoch eine vereinfachte API für die Rückgabe von Routenparametern und kann innerhalb von Layouts verwendet werden. Die Rückgabeform von generateStaticParams
ist ein Array von Segmenten anstelle eines Arrays verschachtelter param
-Objekte oder einer Zeichenkette aufgelöster Pfade.
// `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 ein beschreibenderes generate
ersetzt, das besser allein steht, da getStaticProps
und getServerSideProps
nicht mehr notwendig sind. Das Suffix Paths
wird durch Params
ersetzt, was besser für verschachteltes Routing mit mehreren dynamischen Segmenten geeignet ist.
Ersetzen von fallback
Im pages
-Verzeichnis wird die Eigenschaft fallback
, die von getStaticPaths
zurückgegeben wird, verwendet, um das Verhalten einer Seite zu definieren, die nicht zur Build-Zeit vorgerendert wurde. Diese Eigenschaft kann auf true
gesetzt werden, um eine Fallback-Seite anzuzeigen, während die Seite generiert wird, auf false
, um eine 404-Seite anzuzeigen, oder auf blocking
, um die Seite zur Anforderungszeit zu generieren.
// `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 ingenerateStaticParams
enthalten sind, werden bei Bedarf generiert.false
: Dynamische Segmente, die nicht ingenerateStaticParams
enthalten sind, geben eine 404 zurück.
Dies ersetzt die Option fallback: true | false | 'blocking'
von getStaticPaths
im pages
-Verzeichnis. Die Option fallback: 'blocking'
ist nicht in dynamicParams
enthalten, da der Unterschied zwischen 'blocking'
und true
mit Streaming vernachlässigbar ist.
// `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 ...
}
Wenn dynamicParams
auf true
(Standard) gesetzt ist, wird ein angeforderter Routensegment, der noch nicht generiert wurde, serverseitig gerendert und zwischengespeichert.
Inkrementelle statische Regeneration (getStaticProps
mit revalidate
)
Im pages
-Verzeichnis ermöglicht die Funktion getStaticProps
das Hinzufügen eines revalidate
-Felds, um eine Seite nach einer bestimmten Zeit automatisch neu zu generieren.
// `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 das Abrufen von Daten mit fetch()
revalidate
verwenden, wodurch die Anfrage für die angegebene Anzahl von Sekunden zwischengespeichert wird.
// `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 pages/api
-Verzeichnis ohne Änderungen. Sie wurden jedoch im app
-Verzeichnis durch Route Handler ersetzt.
Route Handler ermöglichen es Ihnen, benutzerdefinierte Anfragehandler für eine bestimmte Route mit den Web-Request- und Response-APIs zu erstellen.
export async function GET(request: Request) {}
export async function GET(request) {}
Gut zu wissen: 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. Erfahren Sie mehr über 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 Styles 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:
module.exports = {
content: [
'./app/**/*.{js,ts,jsx,tsx,mdx}', // <-- Fügen Sie diese Zeile hinzu
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
],
}
Sie müssen auch Ihre globalen Styles in Ihrer app/layout.js
-Datei importieren:
import '../styles/globals.css'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
Erfahren Sie mehr über Styling mit Tailwind CSS
Codemods
Next.js bietet Codemod-Transformationen, um Ihnen beim Upgrade Ihres Codebase zu helfen, wenn eine Funktion veraltet ist. Weitere Informationen finden Sie unter Codemods.