Route
路由可能是 React Router 应用程序中最重要的部分。它们将 URL 段与组件、数据加载和数据变异相结合。通过路由嵌套,复杂的应用程序布局和数据依赖关系变得简单和声明式。
路由是传递给路由器创建函数的对象
const router = createBrowserRouter([
{
// it renders this element
element: <Team />,
// when the URL matches this segment
path: "teams/:teamId",
// with this data loaded before rendering
loader: async ({ request, params }) => {
return fetch(
`/fake/api/teams/${params.teamId}.json`,
{ signal: request.signal }
);
},
// performing this mutation when data is submitted to it
action: async ({ request }) => {
return updateFakeTeam(await request.formData());
},
// and renders this element in case something went wrong
errorElement: <ErrorBoundary />,
},
]);
您也可以使用 JSX 和 createRoutesFromElements
声明您的路由,传递给元素的 props 与路由对象的属性相同
const router = createBrowserRouter(
createRoutesFromElements(
<Route
element={<Team />}
path="teams/:teamId"
loader={async ({ params }) => {
return fetch(
`/fake/api/teams/${params.teamId}.json`
);
}}
action={async ({ request }) => {
return updateFakeTeam(await request.formData());
}}
errorElement={<ErrorBoundary />}
/>
)
);
两种风格都不被反对,行为相同。在本文档的大部分内容中,我们将使用 JSX 风格,因为这是大多数人在 React Router 上下文中习惯使用的风格。
RouterProvider
时,如果您不想指定 React 元素(即 element={<MyComponent />}
),您可以改为指定 Component
(即 Component={MyComponent}
),React Router 会在内部为您调用 createElement
。但是,您应该只在 RouterProvider
应用程序中这样做,因为在 <Routes>
中使用 Component
会降低 React 在渲染之间重用创建元素的能力。
interface RouteObject {
path?: string;
index?: boolean;
children?: RouteObject[];
caseSensitive?: boolean;
id?: string;
loader?: LoaderFunction;
action?: ActionFunction;
element?: React.ReactNode | null;
hydrateFallbackElement?: React.ReactNode | null;
errorElement?: React.ReactNode | null;
Component?: React.ComponentType | null;
HydrateFallback?: React.ComponentType | null;
ErrorBoundary?: React.ComponentType | null;
handle?: RouteObject["handle"];
shouldRevalidate?: ShouldRevalidateFunction;
lazy?: LazyRouteFunction<RouteObject>;
}
path
与 URL 匹配的路径模式,用于确定此路由是否与 URL、链接 href 或表单操作匹配。
如果路径段以 :
开头,则它将成为一个“动态段”。当路由与 URL 匹配时,动态段将从 URL 中解析出来,并作为 params
提供给其他路由器 API。
<Route
// this path will match URLs like
// - /teams/hotspur
// - /teams/real
path="/teams/:teamId"
// the matching param will be available to the loader
loader={({ params }) => {
console.log(params.teamId); // "hotspur"
}}
// and the action
action={({ params }) => {}}
element={<Team />}
/>;
// and the element through `useParams`
function Team() {
let params = useParams();
console.log(params.teamId); // "hotspur"
}
您可以在一个路由路径中拥有多个动态段
<Route path="/c/:categoryId/p/:productId" />;
// both will be available
params.categoryId;
params.productId;
动态段不能是“部分”的
"/teams-:teamId"
"/teams/:teamId"
"/:category--:productId"
"/:productSlug"
您仍然可以支持这样的 URL 模式,您只需要进行一些自己的解析
function Product() {
const { productSlug } = useParams();
const [category, product] = productSlug.split("--");
// ...
}
您可以通过在段的末尾添加 ?
来使路由段可选。
<Route
// this path will match URLs like
// - /categories
// - /en/categories
// - /fr/categories
path="/:lang?/categories"
// the matching param might be available to the loader
loader={({ params }) => {
console.log(params["lang"]); // "en"
}}
// and the action
action={({ params }) => {}}
element={<Categories />}
/>;
// and the element through `useParams`
function Categories() {
let params = useParams();
console.log(params.lang);
}
您也可以拥有可选的静态段
<Route path="/project/task?/:taskId" />
也称为“catchall”和“star”段。如果路由路径模式以 /*
结尾,则它将匹配 /
之后的所有字符,包括其他 /
字符。
<Route
// this path will match URLs like
// - /files
// - /files/one
// - /files/one/two
// - /files/one/two/three
path="/files/*"
// the matching param will be available to the loader
loader={({ params }) => {
console.log(params["*"]); // "one/two"
}}
// and the action
action={({ params }) => {}}
element={<Team />}
/>;
// and the element through `useParams`
function Team() {
let params = useParams();
console.log(params["*"]); // "one/two"
}
您可以解构 *
,您只需要为它分配一个新名称。一个常见的名称是 splat
let { org, "*": splat } = params;
省略路径会使此路由成为“布局路由”。它参与 UI 嵌套,但不会向 URL 添加任何段。
<Route
element={
<div>
<h1>Layout</h1>
<Outlet />
</div>
}
>
<Route path="/" element={<h2>Home</h2>} />
<Route path="/about" element={<h2>About</h2>} />
</Route>
在此示例中,<h1>Layout</h1>
将与每个子路由的 element
prop 一起渲染,通过布局路由的 Outlet。
index
确定路由是否为索引路由。索引路由在其父级的 URL(如默认子路由)处渲染到其父级的 Outlet 中。
<Route path="/teams" element={<Teams />}>
<Route index element={<TeamsIndex />} />
<Route path=":teamId" element={<Team />} />
</Route>
这些特殊路由一开始可能难以理解,因此我们在这里有一个专门针对它们的指南:索引路由。
children
caseSensitive
指示路由是否匹配大小写
<Route caseSensitive path="/wEll-aCtuA11y" />
"wEll-aCtuA11y"
"well-actua11y"
loader
路由加载器在路由渲染之前被调用,并通过 useLoaderData
为元素提供数据。
<Route
path="/teams/:teamId"
loader={({ params }) => {
return fetchTeam(params.teamId);
}}
/>;
function Team() {
let team = useLoaderData();
// ...
}
createBrowserRouter
,这将不会做任何事情
有关更多详细信息,请参阅 加载器 文档。
action
当从 Form、fetcher 或 submission 向路由发送提交时,将调用路由操作。
<Route
path="/teams/:teamId"
action={({ request }) => {
const formData = await request.formData();
return updateTeam(formData);
}}
/>
createBrowserRouter
,这将不会做任何事情
有关更多详细信息,请参阅 操作 文档。
element
/Component
当路由与 URL 匹配时要渲染的 React 元素/组件。
如果您想自己创建 React 元素,请使用 element
<Route path="/for-sale" element={<Properties />} />
否则使用 Component
,React Router 会为您创建 React 元素
<Route path="/for-sale" Component={Properties} />
RouterProvider
的数据路由中选择 Component
API。在 <Routes>
中的 <Route>
上使用此 API 会降低 React 在渲染之间重用创建元素的能力。
errorElement
/ErrorBoundary
当路由在渲染时、在 loader
中或在 action
中抛出异常时,此 React 元素/组件将被渲染,而不是正常的 element
/Component
。
如果您想自己创建 React 元素,请使用 errorElement
<Route
path="/for-sale"
// if this throws an error while rendering
element={<Properties />}
// or this while loading properties
loader={() => loadProperties()}
// or this while creating a property
action={async ({ request }) =>
createProperty(await request.formData())
}
// then this element will render
errorElement={<ErrorBoundary />}
/>
否则使用 ErrorBoundary
,React Router 会为您创建 React 元素
<Route
path="/for-sale"
Component={Properties}
loader={() => loadProperties()}
action={async ({ request }) =>
createProperty(await request.formData())
}
ErrorBoundary={ErrorBoundary}
/>
createBrowserRouter
,这将不会做任何事情
有关更多详细信息,请参阅 errorElement 文档。
hydrateFallbackElement
/HydrateFallback
如果您正在使用 服务器端渲染 并且您正在利用 部分水合,那么您可以指定一个元素/组件,在应用程序的初始水合期间为未水合的路由渲染。
createBrowserRouter
,这将不会做任何事情
clientLoader
功能。大多数 SSR 应用程序不需要利用这些路由属性。
有关更多详细信息,请参阅 hydrateFallbackElement 文档。
handle
任何特定于应用程序的数据。有关详细信息和示例,请参阅 useMatches 文档。
lazy
为了保持应用程序包的大小并支持路由的代码拆分,每个路由都可以提供一个异步函数,该函数解析路由定义(loader
、action
、Component
/element
、ErrorBoundary
/errorElement
等)的非路由匹配部分。
每个 lazy
函数通常会返回动态导入的结果。
let routes = createRoutesFromElements(
<Route path="/" element={<Layout />}>
<Route path="a" lazy={() => import("./a")} />
<Route path="b" lazy={() => import("./b")} />
</Route>
);
然后在您的延迟加载路由模块中,导出您希望为路由定义的属性
export async function loader({ request }) {
let data = await fetchData(request);
return json(data);
}
export function Component() {
let data = useLoaderData();
return (
<>
<h1>You made it!</h1>
<p>{data}</p>
</>
);
}
createBrowserRouter
,这将不会做任何事情
有关更多详细信息,请参阅 延迟加载 文档。