路由匹配
一、 精确匹配( Exact Matching )
精确匹配路由是否匹配完整路径,而非部分前缀。
1. v5
- 默认行为 : 模糊匹配( Prefix Matching )。例如路径
/user会匹配/user/123 - 强制精确匹配 : 需显式添加到
exact属性到<Route>组件
v5 精确匹配
// 仅匹配 /user
<Route path="/user" exact component={User} />
// 匹配 /user/123
<Route path="/user/:id" component={UserProfile} />
- 如果没有
exact,访问/users时可能会同时匹配/和/users,但Switch只渲染第一个匹配项 exact确保只有路径完全相同才匹配
2. v6
- 默认行为 : 严格精确匹配( Strict Exact Matching )。例如
/user仅匹配/user不匹配/user/earthnut - 放宽匹配 : 若需要模糊匹配,可使用
*通配符或通过match对象配置(但通常不推荐)
// v6 默认精确配置(无需 exact )
<Router path="/user" element={<User />} />
<Router path="/user/:id" element={<UserProfile />} />
<Route path="/user/*" element={<UserLayout /> } />
3. v7
- 默认行为 : 延续了 v6 的严格精确匹配
- 调整点 : 优化了对路径参数的匹配逻辑(如可选参数、重复参数),但精确匹配规则与 v6 一致
// v7 与 v6 行为一致。默认精确匹配
<Route path="/user" element={<User />} />
<Route path="/user/:id" element={<UserProfile />} />
二、 动态路由 ( DYnamic Routes )
动态路由用户获取路径中的变量(如用户 ID 、文章 ID ) 。
1. v5
- 定义 : 使用
:定义动态路由 - 获取 : 通过
props.match.params访问
// v5 动态路由定义
<Route path="/user/:id" component={UserProfile} />;
// 组件内获取参数
function UserProfile(props) {
const userId = props.match.params.id; // earthnut
return <div>用户 ID : 「{userId}」</div>;
}
2. v6
- 用法 : 同上
- 参数获取 : 通过
useParams()钩子或useMatch()获取
// v6 动态路由定义
<Route path="/user/:id" element={<UserProfile />} />;
// 组件内获取参数(函数组件)
import { useParams } from 'react-router';
function UserProfile() {
const { id } = useParams(); // 123
return <div>用户 ID: 「{id}」</div>;
}
3. v7
- 依法增强:支持更灵活的动态参数,如:
- 可选参数:
path="/user/:id?"将匹配/user或/user/earthnut - 重复参数:
path="/files/:filename+"将匹配/files/a.txt或/files/a/b.txt - 通配符参数:
path="/search/:query*"将匹配/search/foo或/search/foo/bar
- 可选参数:
- 参数获取 : 与 v6 一致,使用
useParams()
// v7 可选参数示例
<Route path="/user/:id" element={<UserProfile />} />; // 匹配 /user 和 /user/123
// 组件内获取
function UserProfile() {
const { id } = useParams(); // 可能为 undefined (当路径是 /user 时)
return <div>用户 ID : 「{id ?? '未指定'}」</div>;
}
三、嵌套路由 (Nested Routes)
嵌套路由用于在父路由下渲染子路由内容。
1. v5
- 核心依赖 :
<Switch>确保唯一匹配,<Route>的children或render属性定义嵌套 - 实现步骤 :
- 父路由使用
component渲染容器组件 - 子路由作为父组件的子元素,通过
props.children渲染
- 父路由使用
- 不足 : 子路由需要显式声明在父路由的 children 中,且父路由需手动处理
children渲染
v5 嵌套路由示例
<Router>
<Switch>
{/** 父路由 */}
<Route path="/user/:id" component={UserLayout}>
{/** 子路由(需手动包裹在父组件内) */}
<Route path="/user/:id/profile" render={() => <Profile />} />
<Route path="/user/:id/posts" render={() => <Posts />} />
</Route>
</Switch>
</Router>;
// UserLayout 组件需手动渲染子路由内容 (通过 `props.children` )
function UserLayout(props) {
return (
<div>
<h1>用户页面布局</h1>
{/** 子路由再次渲染 */}
{props.children}
</div>
);
}
2. v6
- 核心改进 : 引入
<Routes>代替<Switch>, 并通过<Outlet>标记子路由渲染位置。 - 实现步骤 :
- 父路由通过
element渲染容器组件 - 子路由定义治啊父路由的
children数组中 - 父组件使用
<Outlet>作为子路由的渲染占位符
- 父路由通过
- 优势 : 子路由与父路由解耦,无需手动处理
children,路径自动继承父路由(如/user/:id/profile自动拼接 )
v6 嵌套路由示例
<Router>
<Routes>
{/** 父路由 */}
<Route path="/user/:id" element={<UserLayout />}>
{/** 子路由(直接在父路由的 children 中) */}
<Route path="profile" element={<Profile />} />
<Route path="posts" element={<Posts />} />
</Route>
</Routes>
</Router>;
// UserLayout 组件通过 <Outlet /> 渲染子路由
function UserLayout() {
return (
<div>
<h1>用户页面布局</h1>
{/** 子路由再此处渲染 */}
<Outlet />
</div>
);
}
3. v7
- 核心优化 : 简化嵌套路由的语法,支持更直观的路径定义(相对路径),并强化
<Outlet>的使用 - 实现步骤 :
- 使用
createBrowserRouter()创建路由配置(推荐函数式配置) - 父路由通过
children定义子路由,路径默认相对父路径 - 父组件仍通过
<Outlet />渲染子路由
- 使用
- 新增特性:
- 相对路径 : 子路由的
path可省略父路由前缀(如path: "profile"自动继承父路径/user/:id) - 更灵活的布局 : 支持在任意层级使用
<Outlet>,甚至同一层级多个<Outlet />
- 相对路径 : 子路由的
v7 路由配置(函数式)
import { createBrowserRouter } from 'react-router';
const router = createBrowserRouter([
{
path: '/user/:id',
element: <UserLayout />,
children: [
{
path: 'profile',
element: <Profile />,
},
{
path: 'posts',
element: <Posts />,
},
],
},
]);
// UserLayout 组件与 V6 一致
function UserLayout() {
return (
<div>
<h1> 用户页面布局</h1>
{/** 子路由在此渲染 */}
<Outlet />
</div>
);
}
四、 对比表格
| 特性 | v5 | v6 | v7 |
|---|---|---|---|
| 精准匹配默认行为 | 模糊匹配(需 exact ) | 严格精确匹配 | 严格精确匹配 |
| 动态路由参数获取 | props.match.params | useParams() 钩子 | useParams() 钩子 |
| 嵌套路由核心机制 | <Switch> + props.children | <Routes> + <Outlet /> | <Routes> + <Outlet /> (更简洁) |
| 子路由路径定义 | 绝对路径 | 相对路径 | 更灵活的相对路径 |
| 关键 API 变换 | <Switch> 、 exact | 移除 exact ,引入 <Outlet /> | createBrowserRouter() 、 命名 <Outlet /> |