React Router v6

特性方面 React Router v6 React Router v5
路由容器 <Routes> 替代 <Switch> <Switch>
路由定义 element={<Component />} component={Component}
嵌套路由 更简洁,在父路由中嵌套子路由,父组件使用 <Outlet> 在每层主组件中配置 Route
编程式导航 useNavigate Hook useHistory Hook 或 this.props.history
路由参数获取 使用 useParams 等 Hooks 通过组件的 props.match.params 获取
重定向 <Navigate> 组件 <Redirect> 组件
路由表配置 支持 useRoutes 钩子 使用 react-router-config 包 (需额外安装)
活动链接样式 <NavLink>className 接受函数 <NavLink>activeClassName 属性
相对路径解析 支持 不支持
包大小 更小 (约10.8kB) 较大 (约20.8kB)

1. 安装与基本设置

1.1 安装

使用 npm 或 yarn 安装 react-router-dom(针对 Web 项目):

bash 复制代码
npm install react-router-dom@6

1.2 包裹应用

在你的应用入口文件(如 index.jsmain.jsx)中,使用 BrowserRouterHashRouter 包裹你的根组件,以确保路由功能在整个应用中可用。

jsx 复制代码
// index.js
import React from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom'; // 引入 BrowserRouter
import App from './App';

const container = document.getElementById('root');
const root = createRoot(container);
root.render(
  <BrowserRouter> {/* 使用 BrowserRouter 包裹应用 */}
    <App />
  </BrowserRouter>
);
  • BrowserRouter :利用 HTML5 History API(pushState, replaceState, popstate 事件)来保持 UI 与 URL 的同步。URL 看起来更整洁(如 example.com/path),但需要服务器配置以支持所有路径都返回应用入口页面。
  • HashRouter :使用 URL 的哈希部分(#)进行路由。URL 会包含一个哈希符号(如 example.com/#/path),无需特殊的服务器配置,兼容性更好,但看起来不那么美观。

2. 路由定义与渲染

2.1 <Routes><Route>

在 v6 中,<Switch> 组件被 <Routes> 组件取代<Routes><Route> 必须配合使用,且 <Route> 必须被 <Routes> 包裹。

最大的变化之一是 <Route> 使用 element 属性来指定要渲染的 React 元素 ,取代了 v5 中的 componentrender 属性。

jsx 复制代码
// v5 写法
<Switch>
  <Route exact path="/" component={Home} />
  <Route path="/about" component={About} />
</Switch>

// v6 写法
<Routes>
  <Route path="/" element={<Home />} /> {/* 使用 element 属性 */}
  <Route path="/about" element={<About />} />
  <Route path="users/:userId" element={<UserProfile />} /> {/* 动态路由 */}
  <Route path="*" element={<NotFound />} /> {/* 404 页面,匹配未定义的路由 */}
</Routes>

<Routes> 组件会智能地选择最佳匹配 的路由进行渲染,不再像 v5 的 <Switch> 那样严格依赖代码顺序。这意味着你无需再频繁使用 exact 属性(实际上在 v6 中移除了该属性)。

2.2 使用 useRoutes 定义路由表

v6 推荐使用 useRoutes Hook 来通过一个 JavaScript 对象(路由表) 定义你的路由结构,这使得路由配置更加集中和可读,特别适合中大型项目。

jsx 复制代码
// src/routes/index.js
import { useRoutes } from 'react-router-dom';
import Home from '../pages/Home';
import About from '../pages/About';
import UserLayout from '../layouts/UserLayout';
import UserProfile from '../pages/UserProfile';
import UserSettings from '../pages/UserSettings';
import NotFound from '../pages/NotFound';

export default function Router() {
  return useRoutes([
    {
      path: '/',
      element: <Home />
    },
    {
      path: '/about',
      element: <About />
    },
    {
      path: '/user',
      element: <UserLayout />,
      children: [ // 嵌套路由通过 children 定义
        { index: true, element: <UserDashboard /> }, // index 属性指定默认子路由
        { path: 'profile', element: <UserProfile /> },
        { path: 'settings', element: <UserSettings /> }
      ]
    },
    {
      path: '*',
      element: <NotFound />
    }
  ]);
}

然后在你的 App.js 中使用这个路由表:

jsx 复制代码
// App.js
import Router from './routes';

function App() {
  return (
    <div className="App">
      <Router />
    </div>
  );
}

3. 导航

3.1 <Link><NavLink>

<Link> 是用于导航的主要组件,它最终会被渲染成 <a> 标签,但不会触发整个页面的刷新。

<NavLink> 是一种特殊的 <Link>,它能够知道自己的 active 状态,非常适合用于导航菜单或标签页。

在 v6 中,<NavLink> 的样式设置方式发生了变化:

jsx 复制代码
import { Link, NavLink } from 'react-router-dom';

function Navigation() {
  return (
    <nav>
      <Link to="/">首页</Link> {/* 普通链接 */}
      <NavLink
        to="about"
        className={({ isActive }) => // 通过函数根据 isActive 返回类名
          isActive ? 'active-link' : 'normal-link'
        }
        style={({ isActive }) => ({ // 内联样式也可以
          fontWeight: isActive ? 'bold' : 'normal',
        })}
      >
        关于我们
      </NavLink>
      {/* end 属性可阻止匹配子路由时自身高亮 */}
      <NavLink to="home" end >Home</NavLink>
    </nav>
  );
}

v5 中常用的 activeClassNameactiveStyle 属性在 v6 中已被移除。

3.2 编程式导航:useNavigate

在 v6 中,useHistory Hook 被 useNavigate Hook 取代了。

jsx 复制代码
import { useNavigate } from 'react-router-dom';

function MyComponent() {
  const navigate = useNavigate(); // 获取 navigate 函数

  const handleClick = () => {
    // 跳转到指定路径
    navigate('/home'); 
    // 替换当前历史记录条目
    navigate('/home', { replace: true }); 
    // 导航到上一页(相当于 history.go(-1))
    navigate(-1); 
    // 导航到前两页(相当于 history.go(2))
    navigate(2); 
    
    // 也可以传递状态对象
    navigate('/user', { state: { from: 'homepage' } });
  };

  return (
    <button onClick={handleClick}>跳转到首页</button>
  );
}

navigate 函数支持相对路径 。例如,在 /users 路径下调用 navigate('123') 会跳转到 /users/123

4. 嵌套路由与 <Outlet>

v6 的嵌套路由设计得非常直观。你只需要在父 <Route>(或路由表的 children 数组)中定义子 <Route>,并在父组件中使用 <Outlet> 组件作为子路由的渲染占位符即可。

jsx 复制代码
// 路由定义 - 使用 JSX 形式
<Routes>
  <Route path="user" element={<UserLayout />}>
    <Route index element={<UserDashboard />} /> {/* 默认子路由 */}
    <Route path="profile" element={<UserProfile />} />
    <Route path="settings" element={<UserSettings />} />
  </Route>
</Routes>

// 或者使用路由表形式
{
  path: 'user',
  element: <UserLayout />,
  children: [
    { index: true, element: <UserDashboard /> },
    { path: 'profile', element: <UserProfile /> },
    { path: 'settings', element: <UserSettings /> }
  ]
}
jsx 复制代码
// UserLayout.jsx - 父组件
import { Outlet, Link } from 'react-router-dom'; // 引入 Outlet

function UserLayout() {
  return (
    <div>
      <h2>用户中心</h2>
      <nav>
        <Link to="profile">资料</Link> {/* 相对路径链接到 /user/profile */}
        <Link to="settings">设置</Link> {/* 相对路径链接到 /user/settings */}
      </nav>
      {/* 子路由组件将在此处渲染 */}
      <Outlet /> 
    </div>
  );
}

当访问 /user 时,<UserLayout> 会渲染,其内部的 <Outlet> 则会渲染索引路由 <UserDashboard>。访问 /user/profile 时,<Outlet> 会替换为 <UserProfile>

5. 数据获取与参数处理

5.1 URL 参数:useParams

动态路径参数 (如 :userId)可以通过 useParams Hook 获取。

jsx 复制代码
// 路由定义
<Route path="users/:userId" element={<UserDetail />} />

// UserDetail.jsx
import { useParams } from 'react-router-dom';

function UserDetail() {
  let { userId } = useParams(); // 获取 URL 参数
  // 使用 userId 获取用户数据...
  return <div>用户 ID: {userId}</div>;
}

5.2 查询参数:useSearchParams

URL 查询参数 (如 ?name=John&age=30)可以通过 useSearchParams Hook 获取和设置,它返回一个类似于 useState 的数组。

jsx 复制代码
import { useSearchParams } from 'react-router-dom';

function UserList() {
  const [searchParams, setSearchParams] = useSearchParams();
  const nameFilter = searchParams.get('name'); // 获取 'name' 参数
  const ageFilter = searchParams.get('age');   // 获取 'age' 参数

  const updateFilters = () => {
    // 设置查询参数
    setSearchParams({ name: 'John', age: 30 });
  };

  return (
    <div>
      <div>当前筛选: Name - {nameFilter}, Age - {ageFilter}</div>
      <button onClick={updateFilters}>设置筛选</button>
    </div>
  );
}

5.3 位置状态:useLocationstate

你可以使用 useLocation Hook 来获取当前 location 信息,包括通过 navigate(to, { state })<Link to={pathname} state={state} /> 传递的 state 数据。

jsx 复制代码
import { useLocation } from 'react-router-dom';

function SomeComponent() {
  const location = useLocation();
  console.log(location.pathname); // 当前路径
  console.log(location.state);    // 传递的状态对象,例如 { from: 'homepage' }

  return <div>当前位置: {location.pathname}</div>;
}

状态 state 在页面刷新时,如果使用的是 BrowserRouter 通常会保留,而 HashRouter 可能会导致状态丢失

6. 重定向与特殊功能

6.1 重定向:<Navigate>

在 v6 中,<Redirect><Navigate> 组件取代

jsx 复制代码
import { Navigate } from 'react-router-dom';

function ProtectedRoute({ isAuthenticated, children }) {
  // 如果未登录,则重定向到登录页
  return isAuthenticated ? children : <Navigate to="/login" replace />;
}

// 在路由中使用
<Route 
  path="/dashboard" 
  element={
    <ProtectedRoute isAuthenticated={user != null}>
      <Dashboard />
    </ProtectedRoute>
  } 
/>

replace 属性决定是替换历史记录中的当前条目(true)还是添加一个新条目(false,默认)。

6.2 路由懒加载

你可以使用 React 的 lazySuspense 与 React Router 结合来实现组件的懒加载,优化应用的初始加载速度。

jsx 复制代码
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';

const Home = lazy(() => import('./pages/Home')); // 懒加载 Home 组件
const About = lazy(() => import('./pages/About')); // 懒加载 About 组件

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}> {/* 加载中的回退UI */}
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </Suspense>
  );
}

7. 更多实用技巧与 v5 迁移要点

7.1 路由守卫(受保护路由)

你可以通过组合路由和条件渲染来实现路由守卫,例如验证用户是否已登录。

jsx 复制代码
import { Navigate } from 'react-router-dom';

function ProtectedRoute({ isAuthenticated, children }) {
  return isAuthenticated ? children : <Navigate to="/login" replace state={{ from: location.pathname }} />;
}

// 使用
<Routes>
  <Route path="/dashboard" element={
    <ProtectedRoute isAuthenticated={user != null}>
      <Dashboard />
    </ProtectedRoute>
  } />
  <Route path="/login" element={<Login />} />
</Routes>

7.2 从 v5 迁移到 v6 的主要注意事项

如果你有正在使用 v5 的项目,迁移到 v6 主要涉及以下变化:

  1. <Switch> 替换为 <Routes>
  2. <Route>componentrender 属性改为 element 属性 ,并且传递 JSX 元素(例如 element={<MyComponent />})。
  3. 使用 useNavigate 代替 useHistory 进行编程式导航。
  4. 嵌套路由配置方式变化 ,充分利用 <Route> 嵌套和 <Outlet>
  5. 相对路径链接<Link to="child">navigate('child') 现在默认相对于父路由的路径,而不是绝对路径。
  6. 移除 <Redirect> ,使用 <Navigate> 替代。
  7. 移除 exact 属性,因为 v6 的匹配算法默认就是匹配完整路径。
  8. 路由组件不再自动接收 history, location, match 作为 props ,必须使用 Hooks(如 useNavigate, useLocation, useParams)来获取。
  9. 移除 withRouter 高阶组件,函数组件使用 Hooks,类组件需要自行封装。
  10. <NavLink>activeClassNameactiveStyle 属性被移除 ,改用函数形式的 classNamestyle 属性。

建议仔细阅读官方迁移指南以获取完整信息。

8. 总结

React Router v6 通过更声明式的 API (如 element 属性)、更强大的嵌套路由支持 (通过 <Outlet>)以及与 Hooks 更深入的集成 (如 useNavigate, useParams),极大地改善了开发体验。它的匹配算法更智能,配置也更灵活(支持 JSX 和路由表两种方式)。

React Router v6 官方文档

相关推荐
Eloudy11 小时前
ReAct 原理简介
前端·javascript·人工智能·react.js·agent·gpu
嘟嘟071712 小时前
Next.js 博客左侧笔记列表是怎么从 Redis 渲染出来的?从组件、children 到动态路由一次讲清
redis·react.js·前端工程化
张元清14 小时前
React useInterval Hook:没有过期闭包的 setInterval (2026)
javascript·react.js
MXN_小南学前端14 小时前
React超长文本域中实现“返回顶部”浮动按钮
前端·javascript·react.js
YIAN19 小时前
http无状态?State来展示!从基础路由到鉴权守卫,吃透 SPA 前端路由核心
前端·react.js
console.log('npc')20 小时前
React 19 + Vite 企业级前端项目:从零搭建到规范交付
前端·react.js·前端框架
光影少年20 小时前
react离线缓存、图片缓存方案
开发语言·前端·javascript·react native·react.js·缓存·前端框架
抱抱宝1 天前
Agent-study项目教程(03):手写 Mini-ReAct Agent(不依赖框架)
javascript·人工智能·gpt·react.js·prompt·agent
想要成为糕糕手1 天前
🚀 在浏览器里跑 DeepSeek-R1?WebGPU 端侧推理实战(六)—— 完结篇:消息渲染与流式收尾
react.js·typescript·llm
YIAN1 天前
从 Hash 底层原理到 React Router v6 实战:我学会了什么?
前端·react.js·vue-router