Eine einzige Quelle der Wahrheit für Routenpfade in Next.js-Anwendungen
Erfahren Sie, wie ein zentrales Routing-Modul verschachtelte String-Pfade in Next.js ersetzt, zusammen mit Hilfsfunktionen für dynamische Segmente und gruppierte Route-Exporte.
Die meisten Next.js-Projekte beginnen damit, dass Pfade direkt in Link-Komponenten sowie bei Aufrufen von router.push eingegeben werden. Das ist für die ersten Seiten schnell, doch je größer die Anwendung wird, desto öfter werden dieselben Zeichenketten in Navigationsleisten, Fußern, Umleitungen und E-Mails verwendet, wodurch jedes Umbenennen zu einer mühsamen Suche und Ersetzung wird. Das Problem verschärft sich, wenn KI-Code-Assistenten Teile des Codes erzeugen, da sie selten konsequent dieselbe Konvention befolgen. Indem alle Pfade in ein einziges, eingegebenes Modul überführt werden, lässt sich das beheben.
Was stimmt mit inlineden Pfade nicht
Hier ist eine typische Navigationskomponente, bei der jedes Ziel eine reine Zeichenkette ist:
// ❌ Problematic approach
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Navbar() {
const router = useRouter()
const handleNavigation = () => {
router.push('/dashboard/user/profile')
}
return (
<nav>
<Link href="/about">About</Link>
<Link href="/contact">Contact</Link>
<Link href="/blog">Blog</Link>
<Link href="/dashboard/analytics">Analytics</Link>
<button onClick={handleNavigation}>Profile</button>
</nav>
)
}
Im Moment ist hier noch nichts kaputt, doch mit wachsendem Codebasis und Team tauchen vier Probleme auf.
Umbenennen wird zur Herausforderung
Die Änderung von /blog zu /articles bedeutet, alle Vorkommen in Komponenten, Servercode, Tests und Konfigurationen zu finden. Wenn eines übersehen wird, entsteht ein kaputter Link.
Tippfehler bleiben unentdeckt
Ein Pfad wie /dashbord ist lediglich eine Zeichenkette, weshalb weder der Compiler noch der Linter darauf reagieren. Der Fehler zeigt sich in der Regel als 404-Fehler, manchmal nur in der Produktion.
Der Editor hilft nicht weiter
Weil Routen unstrukturierte Zeichenketten sind, gibt es keine Autocomplete-Funktion, kein „Zur Definition gehen“ und keine Warnung, wenn eine Route nicht mehr existiert.
Mitglieder des Teams geraten auseinander
Unterschiedliche Personen sowie unterschiedliche AI-Anfragen führen dazu, dass derselbe Zielort auf leicht abweichende Weise geschrieben wird: mit oder ohne Schlussstrich, mit manuell erstellter Abfrageseite usw.
Routen zentralisieren in einem Modul
Die Lösung besteht aus einer einzigen Konfigurationsdatei, die von jedem Teil der Anwendung importiert wird.
Wo sich die Datei befindet
Ein Ordner constants oder config neben app und components macht es einfach, sie zu finden:
/src
/app
/components
/constants
└── routes.ts
/lib
Definieren des Routes-Objekts
Das untenstehende Modul gruppiert die Routen nach Bereich. Statische Seiten sind einfache Zeichenketten. Dynamische Seiten sind kleine Funktionen, die den variablen Abschnitt entgegennehmen und den vollständigen Pfad zurückgeben, sodass die Aufrufer niemals URLs manuell zusammenstellen müssen. API-Endpunkte befinden sich unter einem verschachtelten Schlüssel API zur Verwendung in fetch-Aufrufen. Das angehängte as const lässt TypeScript die genauen Literalwerte ableiten und markiert das Objekt als nur zum Lesen, was eine präzise Autocomplete-Funktion ermöglicht und versehentliche Neuzuweisungen verhindert.
// constants/routes.ts
export const ROUTES = {
// Public routes
HOME: '/',
ABOUT: '/about',
CONTACT: '/contact',
PRICING: '/pricing',
// Auth routes
LOGIN: '/auth/login',
REGISTER: '/auth/register',
FORGOT_PASSWORD: '/auth/forgot-password',
// Blog routes
BLOG: '/blog',
BLOG_DETAIL: (slug: string) => `/blog/${slug}`,
BLOG_CATEGORY: (category: string) => `/blog/category/${category}`,
// Dashboard routes
DASHBOARD: '/dashboard',
DASHBOARD_ANALYTICS: '/dashboard/analytics',
DASHBOARD_SETTINGS: '/dashboard/settings',
DASHBOARD_PROFILE: '/dashboard/profile',
// Dynamic user routes
USER_PROFILE: (userId: string) => `/user/${userId}`,
USER_POSTS: (userId: string) => `/user/${userId}/posts`,
// API routes (useful for fetch calls)
API: {
USERS: '/api/users',
POSTS: '/api/posts',
AUTH: '/api/auth',
},
} as const
// Export route groups for better organization
export const AUTH_ROUTES = {
LOGIN: ROUTES.LOGIN,
REGISTER: ROUTES.REGISTER,
FORGOT_PASSWORD: ROUTES.FORGOT_PASSWORD,
} as const
export const DASHBOARD_ROUTES = {
HOME: ROUTES.DASHBOARD,
ANALYTICS: ROUTES.DASHBOARD_ANALYTICS,
SETTINGS: ROUTES.DASHBOARD_SETTINGS,
PROFILE: ROUTES.DASHBOARD_PROFILE,
} as const
Die beiden zusätzlichen Exporte, AUTH_ROUTES und DASHBOARD_ROUTES, sind Ansichten des Hauptobjekts für Code, der sich nur auf einen bestimmten Bereich konzentriert – beispielsweise Middleware zur Absicherung des Dashboards. Da sie auf ROUTES verweisen anstatt wiederholte Zeichenketten zu verwenden, gibt es weiterhin nur eine Definition pro Pfad.
Eine Verbesserung, die in Betracht gezogen werden sollte: Wenn ein Slug oder ID Leerzeichen oder Sonderzeichen enthalten kann, sollte dieser innerhalb des Hilfsfunkts mit encodeURIComponent umschlossen werden, damit jeder Aufrufer automatisch eine sichere URL erhält.
Nutzung der Routen in Komponenten
Die Navbar importiert nun Namen anstelle von Zeichenketten. Sowohl Link als auch imperative Navigationen lesen aus demselben Modul, sodass die beiden niemals unterschiedliche Werte liefern können:
// components/Navbar.tsx
import Link from 'next/link'
import { useRouter } from 'next/router'
import { ROUTES, DASHBOARD_ROUTES } from '@/constants/routes'
export default function Navbar() {
const router = useRouter()
const handleProfileNavigation = () => {
router.push(DASHBOARD_ROUTES.PROFILE)
}
return (
<nav className="flex space-x-4">
<Link href={ROUTES.ABOUT} className="hover:text-blue-500">
About
</Link>
<Link href={ROUTES.CONTACT} className="hover:text-blue-500">
Contact
</Link>
<Link href={ROUTES.BLOG} className="hover:text-blue-500">
Blog
</Link>
<Link href={DASHBOARD_ROUTES.ANALYTICS} className="hover:text-blue-500">
Analytics
</Link>
<button
onClick={handleProfileNavigation}
className="rounded bg-blue-500 px-4 py-2 text-white"
>
Profile
</button>
</nav>
)
}
Bemerken Sie, dass dieses Beispiel useRouter aus next/router importiert, was zum Pages Router gehört. In App Router-Projekten sollte es stattdessen aus next/navigation innerhalb eines Client-Komponenten importiert werden; das Routes-Modul funktioniert in beiden Fällen auf dieselbe Weise.
Dynamische Links erstellen
Dynamische Segmente sind der Bereich, in dem dieses Muster am meisten Vorteile bringt. Die untenstehende Liste verknüpft jeden Beitrag sowie seine Kategorie, indem Hilfsfunktionen mit Daten aus dem Beitrag aufgerufen werden:
// components/BlogList.tsx
import Link from 'next/link'
import { ROUTES } from '@/constants/routes'
interface Post {
slug: string
title: string
category: string
}
interface BlogListProps {
posts: Post[]
}
export default function BlogList({ posts }: BlogListProps) {
return (
<div className="space-y-4">
{posts.map((post) => (
<article key={post.slug} className="rounded border p-4">
<h2 className="text-xl font-bold">
<Link href={ROUTES.BLOG_DETAIL(post.slug)} className="hover:text-blue-500">
{post.title}
</Link>
</h2>
<Link
href={ROUTES.BLOG_CATEGORY(post.category)}
className="text-sm text-gray-500 hover:text-blue-500"
>
Category: {post.category}
</Link>
</article>
))}
</div>
)
}
Falls sich die URL-Struktur der Beiträge jemals ändert – beispielsweise durch Hinzufügen eines Jahressegments – müssen Sie BLOG_DETAIL nur einmal aktualisieren, und alle Aufrufer werden dies befolgen. TypeScript weist außerdem auf jeden Aufruf hin, bei dem die falsche Anzahl an Argumenten übergeben wird.
Vorteile
- Jeder Pfad wird nur einmal definiert und überall wiederverwendet.
Beschränkungen und Alternativen
Eine Konstantendatei überprüft nicht, ob eine entsprechende Seite tatsächlich im Verzeichnis app oder pages existiert. Wenn ein Verzeichnis umbenannt wird, ohne das Modul zu aktualisieren, brechen die Links weiterhin. Next.js bietet außerdem eine Option für typisierte Routen an, die Typen aus dem Dateisystem generiert; prüfen Sie die aktuelle Dokumentation bezüglich des Status dieser Funktion und ob sie zu Ihrer Einrichtung passt. Viele Teams kombinieren beides: generierte Typen für Korrektheit sowie ein Konstantenmodul für lesbare Namen und Hilfsmittel.
Wenn KI-Assistenten Code beitragen, weisen Sie sie in Ihren Projektanweisungen an, von dem routes-Modul zu importieren.
Zusammenfassung
Betrachten Sie URLs als Teil der API Ihrer Anwendung und nicht als verstreute Zeichenketten. Ein typisiertes, zentralisiertes routes-Modul macht Umbenennungen zu einzelnen Änderungen, zeigt Fehler im Editor an und bietet sowohl Menschen als auch Tools einen eindeutigen Ort zum Suchen.