Zum Inhalt springen

Vom Pages Router zum App Router ohne Feature-Stopp

Beide Router laufen nebeneinander in derselben Anwendung, die Migration läuft also Route für Route, während Ihr Team weiter ausliefert. Hier die Reihenfolge und die drei üblichen Fehler.

5 Min. Lesezeit

Die nützlichste Tatsache zu dieser Migration lautet: Sie müssen sich nicht entscheiden. pages/ und app/ laufen in derselben Anwendung, im selben Prozess, gegen dasselbe Deployment. Eine Anfrage wird zuerst gegen app/ geprüft und fällt auf pages/ zurück, wenn nichts passt.

Das heißt: Ein Feature-Stopp ist keine Voraussetzung der Migration. Er ist ein Symptom davon, sie als eine große Änderung statt als vierzig kleine geplant zu haben.

Die Reihenfolge, die funktioniert

Migrieren Sie von den Blättern nach innen, nicht von der Wurzel nach außen.

  1. Zuerst eine Route mit wenig Traffic und wenig Risiko. Etwa /about. Nicht weil sie wichtig wäre, sondern weil sie Sie zwingt, das Layout, den Metadaten-Helfer und die Konventionen fürs Datenholen zu bauen, die Sie überall sonst brauchen - auf einer Seite, deren Ausfall für eine Stunde niemandem auffällt.
  2. Der Rest der statischen Marketing-Routen. Die konvertieren fast mechanisch, und dort ist der Performance-Gewinn am größten.
  3. Dynamische Routen, die nur lesen. Produktseiten, Artikel, Listen. Hier verwandeln Sie getStaticProps und getServerSideProps in async -Komponenten, und das ist der Großteil der mechanischen Arbeit.
  4. Authentifizierte und interaktive Routen. Dashboards, Einstellungen, Checkout. Sie tragen das echte Risiko, weil sie die Mutationen tragen.
  5. Das Root-Layout zuletzt. _app.tsx und _document.tsx früh zu verschieben zwingt jede Route in den neuen Baum, bevor eine davon bereit ist.

Teams, die das umdrehen - weil die Hülle sich wie das Fundament anfühlt -, sind die Teams, die am Ende einfrieren.

Was aus jeder Pages-API wird

Pages RouterApp Router
getStaticPropsasync-Komponente + fetch mit revalidate oder Tag
getServerSidePropsasync-Komponente + cache: 'no-store'
getStaticPathsgenerateStaticParams
_app.tsxapp/layout.tsx
_document.tsxapp/layout.tsx (die html- und body-Tags)
next/headder metadata-Export oder generateMetadata
useRouter().querydie Props params und searchParams
API-RoutenRoute Handler, oder Server Actions für Mutationen

Eine Route wird dabei meist kürzer. Aus diesem:

// pages/products/[slug].tsx
export async function getStaticProps({ params }) {
  const product = await getProduct(params.slug);
  if (!product) return { notFound: true };
  return { props: { product }, revalidate: 3600 };
}
 
export async function getStaticPaths() {
  const products = await getProducts();
  return {
    paths: products.map((p) => ({ params: { slug: p.slug } })),
    fallback: 'blocking',
  };
}
 
export default function ProductPage({ product }) {
  return <Product data={product} />;
}

wird dieses:

// app/products/[slug]/page.tsx
export async function generateStaticParams() {
  const products = await getProducts();
  return products.map((p) => ({ slug: p.slug }));
}
 
export default async function ProductPage({ params }) {
  const { slug } = await params;
  const product = await getProduct(slug);
  if (!product) notFound();
 
  return <Product data={product} />;
}

Beachten Sie await params. In aktuellen Next.js-Versionen sind params und searchParams Promises. Code aus einem zwei Jahre alten Tutorial destrukturiert sie direkt und schlägt auf eine Weise fehl, die wie ein Datenproblem aussieht.

Die drei Stellen, an denen es schiefgeht

Die Hülle use client markieren, damit die Migration kompiliert. Irgendwas im alten Layout nutzt einen Context-Provider, der Build meckert, und jemand setzt 'use client' an den Anfang des Root-Layouts. Die Anwendung kompiliert wieder, jede Route schickt den gesamten Baum an den Browser, und der Hauptnutzen der Migration ist weg, während die Kosten weiter anfallen. Schieben Sie Provider in eine Client-Komponente hinunter, die children umschließt, und lassen Sie das Layout auf dem Server. Das ist dieselbe Disziplin wie in wo use client Sie wirklich kostet.

Die Datenschicht unverändert übernehmen. getServerSideProps lief einmal pro Route, also haben die meisten Codebasen eine einzige Alles-Holen-Funktion pro Seite gebaut. Im App Router kann jede Komponente holen, was sie braucht, und identische Anfragen in einem Render werden für Sie dedupliziert. Die Gott-Funktion funktioniert weiterhin - und lässt die ganze Route weiter auf den langsamsten Aufruf warten, sodass nichts streamt und Suspense Ihnen nichts bringt.

next/head stehen lassen. In app/ ist es still wirkungslos. Kein Fehler, keine Warnung - nur eine Route ohne Titel und ohne Beschreibung, was niemandem auffällt, bis einen Monat später ein SEO-Report kommt. Greppen Sie danach, bevor Sie ausliefern, und konvertieren Sie jede Stelle zum metadata-Export.

Route für Route belegen, dass es gewirkt hat

Bevor Sie eine Route verschieben, halten Sie fest, was sie jetzt tut: LCP und INP aus Felddaten, das gerenderte HTML, Titel und Canonical, das JSON-LD, das First Load JS. Nach dem Verschieben vergleichen Sie. Eine Migration ohne Vorher ist ein Rewrite mit Zusatzschritten.

Die Routen, die wir so verschoben haben, verlieren typischerweise 30-50 % ihres JavaScripts und gewinnen kein Risiko dazu, weil zu keinem Zeitpunkt mehr als eine Route in einem unbekannten Zustand war.

Wenn die Codebasis, die Sie migrieren, zusätzlich eine ist, die niemand anfassen will, liegt darauf ein zweites Problem - eine geerbte Next.js-Codebasis übernehmen beschreibt, was zuerst zu tun ist.

Zurück zu allen Artikeln