路由在 app/routes.ts
中配置。每个路由都有两个必需的部分:一个用于匹配 URL 的 URL 模式,以及一个指向定义其行为的路由模块的文件路径。
import {
type RouteConfig,
route,
} from "@react-router/dev/routes";
export default [
route("some/path", "./some/file.tsx"),
// pattern ^ ^ module file
] satisfies RouteConfig;
这是一个更大的路由配置示例
import {
type RouteConfig,
route,
index,
layout,
prefix,
} from "@react-router/dev/routes";
export default [
index("./home.tsx"),
route("about", "./about.tsx"),
layout("./auth/layout.tsx", [
route("login", "./auth/login.tsx"),
route("register", "./auth/register.tsx"),
]),
...prefix("concerts", [
index("./concerts/home.tsx"),
route(":city", "./concerts/city.tsx"),
route("trending", "./concerts/trending.tsx"),
]),
] satisfies RouteConfig;
如果您更喜欢通过文件命名约定而不是配置来定义路由,则 @react-router/fs-routes
包提供了一种文件系统路由约定。
routes.ts
中引用的文件定义每个路由的行为
route("teams/:teamId", "./team.tsx"),
// route module ^^^^^^^^
这是一个路由模块示例
// provides type safety/inference
import type { Route } from "./+types/team";
// provides `loaderData` to the component
export async function loader({ params }: Route.LoaderArgs) {
let team = await fetchTeam(params.teamId);
return { name: team.name };
}
// renders after the loader is done
export default function Component({
loaderData,
}: Route.ComponentProps) {
return <h1>{loaderData.name}</h1>;
}
路由模块具有更多功能,例如操作、标头和错误边界,但这些将在下一份指南中介绍:路由模块
路由可以嵌套在父路由中。
import {
type RouteConfig,
route,
index,
} from "@react-router/dev/routes";
export default [
// parent route
route("dashboard", "./dashboard.tsx", [
// child routes
index("./home.tsx"),
route("settings", "./settings.tsx"),
]),
] satisfies RouteConfig;
父路由的路径会自动包含在子路由中,因此此配置创建了"/dashboard"
和"/dashboard/settings"
这两个 URL。
子路由通过父路由中的<Outlet/>
进行渲染。
import { Outlet } from "react-router";
export default function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
{/* will either be home.tsx or settings.tsx */}
<Outlet />
</div>
);
}
routes.ts
中的每个路由都嵌套在特殊的app/root.tsx
模块中。
使用layout
,布局路由为其子路由创建新的嵌套,但它们不会向 URL 添加任何片段。它类似于根路由,但可以添加到任何级别。
import {
type RouteConfig,
route,
layout,
index,
prefix,
} from "@react-router/dev/routes";
export default [
layout("./marketing/layout.tsx", [
index("./marketing/home.tsx"),
route("contact", "./marketing/contact.tsx"),
]),
...prefix("projects", [
index("./projects/home.tsx"),
layout("./projects/project-layout.tsx", [
route(":pid", "./projects/project.tsx"),
route(":pid/edit", "./projects/edit-project.tsx"),
]),
]),
] satisfies RouteConfig;
index(componentFile),
索引路由在其父路由的 URL 上(类似于默认子路由)渲染到其父路由的Outlet中。
import {
type RouteConfig,
route,
index,
} from "@react-router/dev/routes";
export default [
// renders into the root.tsx Outlet at /
index("./home.tsx"),
route("dashboard", "./dashboard.tsx", [
// renders into the dashboard.tsx Outlet at /dashboard
index("./dashboard-home.tsx"),
route("settings", "./dashboard-settings.tsx"),
]),
] satisfies RouteConfig;
请注意,索引路由不能有子路由。
使用prefix
,您可以向一组路由添加路径前缀,而无需引入父路由文件。
import {
type RouteConfig,
route,
layout,
index,
prefix,
} from "@react-router/dev/routes";
export default [
layout("./marketing/layout.tsx", [
index("./marketing/home.tsx"),
route("contact", "./marketing/contact.tsx"),
]),
...prefix("projects", [
index("./projects/home.tsx"),
layout("./projects/project-layout.tsx", [
route(":pid", "./projects/project.tsx"),
route(":pid/edit", "./projects/edit-project.tsx"),
]),
]),
] satisfies RouteConfig;
如果路径片段以:
开头,则它将成为“动态片段”。当路由匹配 URL 时,动态片段将从 URL 中解析,并作为params
提供给其他路由器 API。
route("teams/:teamId", "./team.tsx"),
import type { Route } from "./+types/team";
export async function loader({ params }: Route.LoaderArgs) {
// ^? { teamId: string }
}
export default function Component({
params,
}: Route.ComponentProps) {
params.teamId;
// ^ string
}
在一个路由路径中可以有多个动态片段
route("c/:categoryId/p/:productId", "./product.tsx"),
import type { Route } from "./+types/product";
async function loader({ params }: LoaderArgs) {
// ^? { categoryId: string; productId: string }
}
您可以通过在片段末尾添加?
来使路由片段可选。
route(":lang?/categories", "./categories.tsx"),
您也可以拥有可选的静态片段
route("users/:userId/edit?", "./user.tsx");
也称为“通配符”和“星号”片段。如果路由路径模式以/*
结尾,则它将匹配/
后面的任何字符,包括其他/
字符。
route("files/*", "./files.tsx"),
export async function loader({ params }: Route.LoaderArgs) {
// params["*"] will contain the remaining URL after files/
}
您可以解构*
,只需为其分配一个新名称即可。一个常用的名称是splat
const { "*": splat } = params;
您还可以使用与 URL 匹配的组件来匹配组件树中任何位置的元素
import { Routes, Route } from "react-router";
function Wizard() {
return (
<div>
<h1>Some Wizard with Steps</h1>
<Routes>
<Route index element={<StepOne />} />
<Route path="step-2" element={<StepTwo />} />
<Route path="step-3" element={<StepThree />}>
</Routes>
</div>
);
}
请注意,这些路由不参与数据加载、操作、代码分割或任何其他路由模块功能,因此它们的用例比路由模块更有限。
下一步:路由模块