跳到主要内容

路由匹配

一、 精确匹配( 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>childrenrender 属性定义嵌套
  • 实现步骤
    • 父路由使用 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>
);
}

四、 对比表格

特性v5v6v7
精准匹配默认行为模糊匹配(需 exact严格精确匹配严格精确匹配
动态路由参数获取props.match.paramsuseParams() 钩子useParams() 钩子
嵌套路由核心机制<Switch> + props.children<Routes> + <Outlet /><Routes> + <Outlet /> (更简洁)
子路由路径定义绝对路径相对路径更灵活的相对路径
关键 API 变换<Switch>exact移除 exact ,引入 <Outlet />createBrowserRouter() 、 命名 <Outlet />