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 官方文档

相关推荐
程序员海军1 天前
2025年上半年前端技术圈生态总结分享
前端·vue.js·react.js
光头程序员1 天前
vite_react 插件 find_code 最终版本
前端·javascript·react.js
_Kayo_1 天前
React 学习笔记4 Diffing/脚手架
笔记·学习·react.js
山雀~1 天前
React实现列表拖拽排序
前端·react.js·前端框架
前端世界1 天前
前端路由切换不再白屏:React/Vue 实战优化全攻略(含可运行 Demo)
前端·vue.js·react.js
Java陈序员1 天前
GitHub 星标太多管不过来?这款 AI 工具帮你一键整理、智能搜索!
react.js·openai·vite
日月晨曦1 天前
React 在线 playground 实现指南:让代码在浏览器里「原地爆炸」的黑科技
前端·react.js
GISBox1 天前
GISBox矢量服务使用指南:从数据导入到服务发布
react.js·json·gis
江城开朗的豌豆1 天前
Redux 到底香不香?手把手教你状态管理与更新!
前端·javascript·react.js
江城开朗的豌豆1 天前
React 性能优化必杀技:让你的应用飞起来!
前端·javascript·react.js