Tipp
Code-basiertes Routing wird für die meisten Anwendungen nicht empfohlen. Es wird empfohlen, stattdessen Datei-basiertes Routing zu verwenden.
Code-basiertes Routing unterscheidet sich nicht von dateibasiertem Routing darin, dass es dasselbe Routenbaumkonzept verwendet, um passende Routen zu organisieren, abzugleichen und in einen Komponentenbaum zu integrieren. Der einzige Unterschied besteht darin, dass Sie anstelle des Dateisystems zum Organisieren Ihrer Routen Code verwenden.
Betrachten wir denselben Routenbaum aus der Anleitung Routenbäume & Verschachtelung und wandeln ihn in codebasiertes Routing um
Hier ist die dateibasierte Version
routes/
├── __root.tsx
├── index.tsx
├── about.tsx
├── posts/
│ ├── index.tsx
│ ├── $postId.tsx
├── posts.$postId.edit.tsx
├── settings/
│ ├── profile.tsx
│ ├── notifications.tsx
├── _pathlessLayout.tsx
├── _pathlessLayout/
│ ├── route-a.tsx
├── ├── route-b.tsx
├── files/
│ ├── $.tsx
routes/
├── __root.tsx
├── index.tsx
├── about.tsx
├── posts/
│ ├── index.tsx
│ ├── $postId.tsx
├── posts.$postId.edit.tsx
├── settings/
│ ├── profile.tsx
│ ├── notifications.tsx
├── _pathlessLayout.tsx
├── _pathlessLayout/
│ ├── route-a.tsx
├── ├── route-b.tsx
├── files/
│ ├── $.tsx
Und hier ist eine zusammengefasste codebasierte Version
import { createRootRoute, createRoute } from '@tanstack/react-router'
const rootRoute = createRootRoute()
const indexRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/',
})
const aboutRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'about',
})
const postsRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'posts',
})
const postsIndexRoute = createRoute({
getParentRoute: () => postsRoute,
path: '/',
})
const postRoute = createRoute({
getParentRoute: () => postsRoute,
path: '$postId',
})
const postEditorRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'posts/$postId/edit',
})
const settingsRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'settings',
})
const profileRoute = createRoute({
getParentRoute: () => settingsRoute,
path: 'profile',
})
const notificationsRoute = createRoute({
getParentRoute: () => settingsRoute,
path: 'notifications',
})
const pathlessLayoutRoute = createRoute({
getParentRoute: () => rootRoute,
id: 'pathlessLayout',
})
const pathlessLayoutARoute = createRoute({
getParentRoute: () => pathlessLayoutRoute,
path: 'route-a',
})
const pathlessLayoutBRoute = createRoute({
getParentRoute: () => pathlessLayoutRoute,
path: 'route-b',
})
const filesRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'files/$',
})
import { createRootRoute, createRoute } from '@tanstack/react-router'
const rootRoute = createRootRoute()
const indexRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/',
})
const aboutRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'about',
})
const postsRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'posts',
})
const postsIndexRoute = createRoute({
getParentRoute: () => postsRoute,
path: '/',
})
const postRoute = createRoute({
getParentRoute: () => postsRoute,
path: '$postId',
})
const postEditorRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'posts/$postId/edit',
})
const settingsRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'settings',
})
const profileRoute = createRoute({
getParentRoute: () => settingsRoute,
path: 'profile',
})
const notificationsRoute = createRoute({
getParentRoute: () => settingsRoute,
path: 'notifications',
})
const pathlessLayoutRoute = createRoute({
getParentRoute: () => rootRoute,
id: 'pathlessLayout',
})
const pathlessLayoutARoute = createRoute({
getParentRoute: () => pathlessLayoutRoute,
path: 'route-a',
})
const pathlessLayoutBRoute = createRoute({
getParentRoute: () => pathlessLayoutRoute,
path: 'route-b',
})
const filesRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'files/$',
})
Alle anderen Routen außer der Root-Route werden mit der Funktion createRoute konfiguriert
const route = createRoute({
getParentRoute: () => rootRoute,
path: '/posts',
component: PostsComponent,
})
const route = createRoute({
getParentRoute: () => rootRoute,
path: '/posts',
component: PostsComponent,
})
Die Option getParentRoute ist eine Funktion, die die übergeordnete Route der zu erstellenden Route zurückgibt.
❓❓❓ "Moment, Sie lassen mich die übergeordnete Route für jede von mir erstellte Route übergeben?"
Absolut! Der Grund für die Übergabe der übergeordneten Route hat alles mit der magischen Typsicherheit von TanStack Router zu tun. Ohne die übergeordnete Route wüsste TypeScript nicht, welche Typen es Ihrer Route zuweisen soll!
Wichtig
Für jede Route, die NICHT die Root-Route oder eine Pfadlose Layout-Route ist, ist eine path-Option erforderlich. Dies ist der Pfad, der mit dem URL-Pfad abgeglichen wird, um zu bestimmen, ob die Route eine Übereinstimmung ist.
Wenn Sie die path-Option auf einer Route konfigurieren, ignoriert diese führende und nachgestellte Schrägstriche (dies schließt "Index"-Routenpfade / nicht ein). Sie können sie einfügen, wenn Sie möchten, aber sie werden intern von TanStack Router normalisiert. Hier ist eine Tabelle mit gültigen Pfaden und wie sie normalisiert werden
| Pfad | Normalisierter Pfad |
|---|---|
| / | / |
| /about | about |
| about/ | about |
| about | about |
| $ | $ |
| /$ | $ |
| /$/ | $ |
Beim Erstellen eines Routenbaums im Code reicht es nicht aus, die übergeordnete Route jeder Route zu definieren. Sie müssen auch den endgültigen Routenbaum erstellen, indem Sie jede Route zum children-Array ihrer übergeordneten Route hinzufügen. Dies liegt daran, dass der Routenbaum nicht automatisch für Sie erstellt wird, wie es beim dateibasierten Routing der Fall ist.
/* prettier-ignore */
const routeTree = rootRoute.addChildren([
indexRoute,
aboutRoute,
postsRoute.addChildren([
postsIndexRoute,
postRoute,
]),
postEditorRoute,
settingsRoute.addChildren([
profileRoute,
notificationsRoute,
]),
pathlessLayoutRoute.addChildren([
pathlessLayoutARoute,
pathlessLayoutBRoute,
]),
filesRoute.addChildren([
fileRoute,
]),
])
/* prettier-ignore-end */
/* prettier-ignore */
const routeTree = rootRoute.addChildren([
indexRoute,
aboutRoute,
postsRoute.addChildren([
postsIndexRoute,
postRoute,
]),
postEditorRoute,
settingsRoute.addChildren([
profileRoute,
notificationsRoute,
]),
pathlessLayoutRoute.addChildren([
pathlessLayoutARoute,
pathlessLayoutBRoute,
]),
filesRoute.addChildren([
fileRoute,
]),
])
/* prettier-ignore-end */
Aber bevor Sie den Routenbaum erstellen können, müssen Sie verstehen, wie die Routing-Konzepte für codebasiertes Routing funktionieren.
Glauben Sie es oder nicht, dateibasiertes Routing ist wirklich eine Obermenge des codebasierten Routings und verwendet das Dateisystem und eine kleine Code-Generierungsabstraktion darüber, um diese oben gezeigte Struktur automatisch zu generieren.
Wir gehen davon aus, dass Sie die Anleitung Routing-Konzepte gelesen haben und mit jedem dieser Hauptkonzepte vertraut sind
Sehen wir uns nun an, wie jede dieser Routentypen im Code erstellt wird.
Das Erstellen einer Root-Route im codebasierten Routing ist glücklicherweise dasselbe wie beim dateibasierten Routing. Rufen Sie die Funktion createRootRoute() auf.
Im Gegensatz zum dateibasierten Routing müssen Sie die Root-Route nicht exportieren, wenn Sie nicht möchten. Es ist sicherlich nicht ratsam, einen ganzen Routenbaum und eine Anwendung in einer einzigen Datei zu erstellen (obwohl Sie dies tun können und wir es in den Beispielen tun, um Routing-Konzepte kurz zu demonstrieren).
// Standard root route
import { createRootRoute } from '@tanstack/react-router'
const rootRoute = createRootRoute()
// Root route with Context
import { createRootRouteWithContext } from '@tanstack/react-router'
import type { QueryClient } from '@tanstack/react-query'
export interface MyRouterContext {
queryClient: QueryClient
}
const rootRoute = createRootRouteWithContext<MyRouterContext>()
// Standard root route
import { createRootRoute } from '@tanstack/react-router'
const rootRoute = createRootRoute()
// Root route with Context
import { createRootRouteWithContext } from '@tanstack/react-router'
import type { QueryClient } from '@tanstack/react-query'
export interface MyRouterContext {
queryClient: QueryClient
}
const rootRoute = createRootRouteWithContext<MyRouterContext>()
Um mehr über Kontext in TanStack Router zu erfahren, siehe die Anleitung Router Context.
Um eine einfache Route zu erstellen, übergeben Sie einfach einen normalen path-String an die Funktion createRoute
const aboutRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'about',
})
const aboutRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'about',
})
Sehen Sie, so einfach ist das! Die aboutRoute stimmt mit der URL /about überein.
Im Gegensatz zum dateibasierten Routing, das den index-Dateinamen verwendet, um eine Index-Route zu bezeichnen, verwendet codebasiertes Routing einen einzelnen Schrägstrich /, um eine Index-Route zu bezeichnen. Zum Beispiel würde die Datei posts.index.tsx aus unserem obigen Beispiel-Routenbaum im codebasierten Routing wie folgt dargestellt werden
const postsRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'posts',
})
const postsIndexRoute = createRoute({
getParentRoute: () => postsRoute,
// Notice the single slash `/` here
path: '/',
})
const postsRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'posts',
})
const postsIndexRoute = createRoute({
getParentRoute: () => postsRoute,
// Notice the single slash `/` here
path: '/',
})
Die postsIndexRoute stimmt also mit der URL /posts/ (oder /posts) überein.
Dynamische Routensegmente funktionieren im codebasierten Routing genau wie im dateibasierten Routing. Präfixen Sie einfach ein Segment des Pfads mit einem $ und es wird in das params-Objekt des loader oder component der Route erfasst werden
const postIdRoute = createRoute({
getParentRoute: () => postsRoute,
path: '$postId',
// In a loader
loader: ({ params }) => fetchPost(params.postId),
// Or in a component
component: PostComponent,
})
function PostComponent() {
const { postId } = postIdRoute.useParams()
return <div>Post ID: {postId}</div>
}
const postIdRoute = createRoute({
getParentRoute: () => postsRoute,
path: '$postId',
// In a loader
loader: ({ params }) => fetchPost(params.postId),
// Or in a component
component: PostComponent,
})
function PostComponent() {
const { postId } = postIdRoute.useParams()
return <div>Post ID: {postId}</div>
}
Tipp
Wenn Ihre Komponente code-gesplittet ist, können Sie die getRouteApi-Funktion verwenden, um den Import der postIdRoute-Konfiguration zu vermeiden, um Zugriff auf den typisierten useParams()-Hook zu erhalten.
Wie erwartet funktionieren Splat/Catch-all-Routen auch im codebasierten Routing wie im dateibasierten Routing. Präfixen Sie einfach ein Segment des Pfads mit einem $ und es wird in das params-Objekt unter dem Schlüssel _splat erfasst werden
const filesRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'files',
})
const fileRoute = createRoute({
getParentRoute: () => filesRoute,
path: '$',
})
const filesRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'files',
})
const fileRoute = createRoute({
getParentRoute: () => filesRoute,
path: '$',
})
Für die URL /documents/hello-world sieht das params-Objekt so aus
{
'_splat': 'documents/hello-world'
}
{
'_splat': 'documents/hello-world'
}
Layout-Routen sind Routen, die ihre Kinder in eine Layout-Komponente einschließen. Im codebasierten Routing können Sie eine Layout-Route erstellen, indem Sie einfach eine Route unter einer anderen Route verschachteln
const postsRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'posts',
component: PostsLayoutComponent, // The layout component
})
function PostsLayoutComponent() {
return (
<div>
<h1>Posts</h1>
<Outlet />
</div>
)
}
const postsIndexRoute = createRoute({
getParentRoute: () => postsRoute,
path: '/',
})
const postsCreateRoute = createRoute({
getParentRoute: () => postsRoute,
path: 'create',
})
const routeTree = rootRoute.addChildren([
// The postsRoute is the layout route
// Its children will be nested under the PostsLayoutComponent
postsRoute.addChildren([postsIndexRoute, postsCreateRoute]),
])
const postsRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'posts',
component: PostsLayoutComponent, // The layout component
})
function PostsLayoutComponent() {
return (
<div>
<h1>Posts</h1>
<Outlet />
</div>
)
}
const postsIndexRoute = createRoute({
getParentRoute: () => postsRoute,
path: '/',
})
const postsCreateRoute = createRoute({
getParentRoute: () => postsRoute,
path: 'create',
})
const routeTree = rootRoute.addChildren([
// The postsRoute is the layout route
// Its children will be nested under the PostsLayoutComponent
postsRoute.addChildren([postsIndexRoute, postsCreateRoute]),
])
Nun rendern sowohl die postsIndexRoute als auch die postsCreateRoute ihre Inhalte innerhalb der PostsLayoutComponent
// URL: /posts
<PostsLayoutComponent>
<PostsIndexComponent />
</PostsLayoutComponent>
// URL: /posts/create
<PostsLayoutComponent>
<PostsCreateComponent />
</PostsLayoutComponent>
// URL: /posts
<PostsLayoutComponent>
<PostsIndexComponent />
</PostsLayoutComponent>
// URL: /posts/create
<PostsLayoutComponent>
<PostsCreateComponent />
</PostsLayoutComponent>
Im dateibasierten Routing wird eine pfadloses Layout-Route mit einem _ als Präfix versehen, aber im codebasierten Routing ist dies einfach eine Route mit einer id anstelle einer path-Option. Das liegt daran, dass codebasiertes Routing das Dateisystem nicht zur Organisation von Routen verwendet, daher ist es nicht notwendig, eine Route mit einem _ zu präfixen, um anzuzeigen, dass sie keinen Pfad hat.
const pathlessLayoutRoute = createRoute({
getParentRoute: () => rootRoute,
id: 'pathlessLayout',
component: PathlessLayoutComponent,
})
function PathlessLayoutComponent() {
return (
<div>
<h1>Pathless Layout</h1>
<Outlet />
</div>
)
}
const pathlessLayoutARoute = createRoute({
getParentRoute: () => pathlessLayoutRoute,
path: 'route-a',
})
const pathlessLayoutBRoute = createRoute({
getParentRoute: () => pathlessLayoutRoute,
path: 'route-b',
})
const routeTree = rootRoute.addChildren([
// The pathless layout route has no path, only an id
// So its children will be nested under the pathless layout route
pathlessLayoutRoute.addChildren([pathlessLayoutARoute, pathlessLayoutBRoute]),
])
const pathlessLayoutRoute = createRoute({
getParentRoute: () => rootRoute,
id: 'pathlessLayout',
component: PathlessLayoutComponent,
})
function PathlessLayoutComponent() {
return (
<div>
<h1>Pathless Layout</h1>
<Outlet />
</div>
)
}
const pathlessLayoutARoute = createRoute({
getParentRoute: () => pathlessLayoutRoute,
path: 'route-a',
})
const pathlessLayoutBRoute = createRoute({
getParentRoute: () => pathlessLayoutRoute,
path: 'route-b',
})
const routeTree = rootRoute.addChildren([
// The pathless layout route has no path, only an id
// So its children will be nested under the pathless layout route
pathlessLayoutRoute.addChildren([pathlessLayoutARoute, pathlessLayoutBRoute]),
])
Nun rendern sowohl /route-a als auch /route-b ihre Inhalte innerhalb der PathlessLayoutComponent
// URL: /route-a
<PathlessLayoutComponent>
<RouteAComponent />
</PathlessLayoutComponent>
// URL: /route-b
<PathlessLayoutComponent>
<RouteBComponent />
</PathlessLayoutComponent>
// URL: /route-a
<PathlessLayoutComponent>
<RouteAComponent />
</PathlessLayoutComponent>
// URL: /route-b
<PathlessLayoutComponent>
<RouteBComponent />
</PathlessLayoutComponent>
Das Erstellen nicht verschachtelter Routen im codebasierten Routing erfordert nicht die Verwendung eines nachgestellten _ im Pfad, erfordert jedoch, dass Sie Ihre Route und Ihren Routenbaum mit den richtigen Pfaden und Verschachtelungen erstellen. Betrachten wir den Routenbaum, bei dem der Beitragseditor nicht unter der Beiträge-Route verschachtelt sein soll
Um dies zu tun, müssen wir eine separate Route für den Beitragseditor erstellen und den gesamten Pfad in der path-Option vom Stammverzeichnis aus einfügen, wo die Route verschachtelt werden soll (in diesem Fall das Stammverzeichnis)
// The posts editor route is nested under the root route
const postEditorRoute = createRoute({
getParentRoute: () => rootRoute,
// The path includes the entire path we need to match
path: 'posts/$postId/edit',
})
const postsRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'posts',
})
const postRoute = createRoute({
getParentRoute: () => postsRoute,
path: '$postId',
})
const routeTree = rootRoute.addChildren([
// The post editor route is nested under the root route
postEditorRoute,
postsRoute.addChildren([postRoute]),
])
// The posts editor route is nested under the root route
const postEditorRoute = createRoute({
getParentRoute: () => rootRoute,
// The path includes the entire path we need to match
path: 'posts/$postId/edit',
})
const postsRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'posts',
})
const postRoute = createRoute({
getParentRoute: () => postsRoute,
path: '$postId',
})
const routeTree = rootRoute.addChildren([
// The post editor route is nested under the root route
postEditorRoute,
postsRoute.addChildren([postRoute]),
])
Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.