React Router v6: 完整介绍与使用指南 – wiki词典


React Router v6: 完整介绍与使用指南

在现代单页应用 (SPA) 中,路由是不可或缺的一部分。它允许用户在应用的不同视图之间导航,而无需重新加载整个页面,从而提供流畅的用户体验。对于 React 应用来说,React Router 是事实上的标准路由解决方案。

自 React Router v6 发布以来,它引入了许多激动人心的新特性、更简洁的 API 和性能优化,使得路由的设置和使用变得更加直观和高效。本文将深入探讨 React Router v6 的核心概念、API 以及如何在你的 React 项目中有效地使用它。

什么是 React Router?

React Router 是一个功能强大且灵活的库,它允许你将 URL 与 React 组件同步。它提供了一套声明式组件,让你可以在应用中轻松地定义路由、导航链接以及处理路由参数等。

为什么选择 React Router v6?

相较于之前的版本,React Router v6 进行了大幅度的改进:

  • 更小的包体积:移除了不常用的 API,减少了最终打包文件的大小。
  • 更简洁的 API:例如,SwitchRoutes 取代,useHistoryuseNavigate 取代,并且路由的定义方式更具表现力。
  • 嵌套路由的改进:嵌套路由的处理更加自然和直观。
  • 相对路径:支持在 RouteLink 中使用相对路径,提高了路由的灵活性。
  • 新的 Hooks:引入了一系列新的 Hooks(如 useRoutes, useMatch),进一步简化了路由逻辑。

安装

首先,你需要在你的 React 项目中安装 React Router v6。

“`bash
npm install react-router-dom

或者

yarn add react-router-dom
“`

react-router-dom 是用于 Web 应用程序的包,它包含了核心的 react-router 功能以及针对 DOM 环境的特定组件(如 BrowserRouter)。

核心概念与组件

React Router v6 的核心思想是组件化路由。一切皆组件,包括你的路由配置。

1. BrowserRouter

BrowserRouter 是最常用的路由容器。它使用 HTML5 历史 API (pushState, replaceState, popstate) 来保持 UI 与 URL 的同步。通常,你会在应用的根组件中包裹你的整个应用。

“`jsx
// src/index.js 或 src/App.js
import React from ‘react’;
import ReactDOM from ‘react-dom/client’;
import { BrowserRouter } from ‘react-router-dom’;
import App from ‘./App’;

const root = ReactDOM.createRoot(document.getElementById(‘root’));
root.render(

{/ 包裹整个应用 /}



);
“`

2. Routes

Routes 组件取代了 v5 中的 Switch。它的作用是遍历其所有的 Route 子元素,并渲染第一个匹配当前 URL 的 Route

3. Route

Route 组件用于定义单个路由。它接受两个主要的 props:

  • path: 匹配的 URL 路径。
  • element: 当 path 匹配时要渲染的 React 元素。

“`jsx
import { Routes, Route } from ‘react-router-dom’;
import Home from ‘./pages/Home’;
import About from ‘./pages/About’;
import Contact from ‘./pages/Contact’;

function App() {
return (

} />
} />
} />

);
}
“`

4. Link

Link 组件是用于在应用中创建导航链接的首选方式。它会渲染一个 <a> 标签,但会阻止浏览器默认的页面刷新行为,而是由 React Router 接管导航。

“`jsx
import { Link } from ‘react-router-dom’;

function Navigation() {
return (

);
}
“`

5. NavLink

NavLink 是一个特殊版本的 Link,它允许你在当前路由匹配时,为链接添加 active 类或应用自定义样式,常用于导航菜单高亮当前页。

“`jsx
import { NavLink } from ‘react-router-dom’;

function Navigation() {
return (

);
}
“`

NavLinkclassNamestyle props 可以接受一个函数,该函数会接收一个包含 isActive 属性的对象。

6. useNavigate Hook

useNavigate Hook 允许你在函数组件中进行编程式导航(即通过代码而不是点击 Link 来改变路由)。它返回一个函数,调用这个函数并传入目标路径即可实现导航。

“`jsx
import { useNavigate } from ‘react-router-dom’;

function Dashboard() {
const navigate = useNavigate();

const handleGoToSettings = () => {
navigate(‘/settings’);
};

const handleGoBack = () => {
navigate(-1); // 相当于浏览器后退
};

return (

Dashboard


);
}
“`

你也可以传递一个对象作为第二个参数,来传递 state:

jsx
navigate('/user/123', { state: { from: 'dashboard' } });

7. useParams Hook

useParams Hook 用于访问当前 URL 中的动态路由参数。

“`jsx
// 定义路由
} />

// 在 UserProfile 组件中
import { useParams } from ‘react-router-dom’;

function UserProfile() {
const { id } = useParams(); // 获取 URL 中的 :id 参数

return (

User Profile

User ID: {id}

);
}
“`

8. useLocation Hook

useLocation Hook 返回一个 location 对象,其中包含当前 URL 的信息,例如路径名 (pathname)、查询字符串 (search) 和 state (state)。

“`jsx
import { useLocation } from ‘react-router-dom’;

function CurrentPageInfo() {
const location = useLocation();

// URL: /products?category=electronics&page=1
// location.pathname: “/products”
// location.search: “?category=electronics&page=1”
// location.state: (如果通过 navigate 传递了 state)

return (

Current Path: {location.pathname}

Query String: {location.search}

{location.state &&

State: {JSON.stringify(location.state)}

}

);
}
“`

要解析查询字符串,你可以使用原生的 URLSearchParams API。

jsx
const queryParams = new URLSearchParams(location.search);
const category = queryParams.get('category'); // "electronics"

9. useMatch Hook

useMatch Hook 允许你检查一个路径是否与当前 URL 匹配,并返回匹配对象(如果匹配)或 null(如果不匹配)。这在某些需要手动判断路由匹配情况的场景中很有用。

“`jsx
import { useMatch } from ‘react-router-dom’;

function CustomNavLink({ to, children }) {
const match = useMatch(to);
const isActive = match !== null;

return (

{children}

);
}
“`

基本使用示例

让我们创建一个简单的应用来演示上述概念:

“`jsx
// src/pages/Home.js
import React from ‘react’;

function Home() {
return

Home Page

;
}
export default Home;

// src/pages/About.js
import React from ‘react’;

function About() {
return

About Us Page

;
}
export default About;

// src/pages/Contact.js
import React from ‘react’;

function Contact() {
return

Contact Us Page

;
}
export default Contact;

// src/App.js
import React from ‘react’;
import { Routes, Route, Link, NavLink } from ‘react-router-dom’;
import Home from ‘./pages/Home’;
import About from ‘./pages/About’;
import Contact from ‘./pages/Contact’;
import UserProfile from ‘./pages/UserProfile’; // 稍后创建

function App() {
return (

  <hr />

  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="/about" element={<About />} />
    <Route path="/contact" element={<Contact />} />
    <Route path="/users/:id" element={<UserProfile />} />
  </Routes>
</div>

);
}

export default App;

// src/index.js (如上所示,包裹 App 组件)
“`

嵌套路由

React Router v6 对嵌套路由的处理更加优雅。你可以在父 Routeelement 中渲染一个 Outlet 组件,并在子 Route 中定义具体的子路由。

“`jsx
// src/pages/DashboardLayout.js
import React from ‘react’;
import { Outlet, NavLink } from ‘react-router-dom’;

function DashboardLayout() {
return (

Dashboard


{/ 子路由内容将在这里渲染 /}

);
}
export default DashboardLayout;

// src/pages/DashboardOverview.js
import React from ‘react’;

function DashboardOverview() {
return

Dashboard Overview

;
}
export default DashboardOverview;

// src/pages/DashboardSettings.js
import React from ‘react’;

function DashboardSettings() {
return

Dashboard Settings

;
}
export default DashboardSettings;

// src/pages/DashboardReports.js
import React from ‘react’;

function DashboardReports() {
return

Dashboard Reports

;
}
export default DashboardReports;

// src/App.js (更新路由配置)
import React from ‘react’;
import { Routes, Route, Link, NavLink } from ‘react-router-dom’;
import Home from ‘./pages/Home’;
import About from ‘./pages/About’;
import Contact from ‘./pages/Contact’;
import UserProfile from ‘./pages/UserProfile’;
import DashboardLayout from ‘./pages/DashboardLayout’;
import DashboardOverview from ‘./pages/DashboardOverview’;
import DashboardSettings from ‘./pages/DashboardSettings’;
import DashboardReports from ‘./pages/DashboardReports’;

function App() {
return (

  <hr />

  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="/about" element={<About />} />
    <Route path="/contact" element={<Contact />} />
    <Route path="/users/:id" element={<UserProfile />} />

    {/* 嵌套路由 */}
    <Route path="/dashboard" element={<DashboardLayout />}>
      {/* 默认子路由,当访问 /dashboard 时渲染 */}
      <Route index element={<DashboardOverview />} /> 
      <Route path="overview" element={<DashboardOverview />} />
      <Route path="settings" element={<DashboardSettings />} />
      <Route path="reports" element={<DashboardReports />} />
    </Route>
  </Routes>
</div>

);
}

export default App;
“`

关键点:

  • Routepath 不需要以 /* 结尾。
  • Routepath 是相对于父路由的,例如 settings 会匹配 /dashboard/settings
  • index prop 用于定义父路由的默认子路由。当访问 /dashboard 时,DashboardOverview 会被渲染。
  • Outlet 组件是渲染子路由内容的占位符。

未找到 (404) 路由

处理用户访问不存在的 URL 时的情况。你可以在 Routes 的末尾添加一个 path="*"Route 来捕获所有不匹配的路径。

“`jsx
// src/pages/NotFound.js
import React from ‘react’;
import { Link } from ‘react-router-dom’;

function NotFound() {
return (

404 – Page Not Found

The page you are looking for does not exist.

Go to Home

);
}
export default NotFound;

// src/App.js (更新路由配置)
// …

{/ … 其他路由 … /}
} /> {/ 404 路由放在最后 /}

// …
“`

重要提示: 404 路由应该放在所有其他路由定义的最后,因为 Routes 组件会渲染第一个匹配的路由。

认证/受保护路由

在许多应用中,某些页面只允许登录用户访问。你可以创建一个高阶组件 (HOC) 或一个包装组件来处理认证逻辑。

“`jsx
// src/components/RequireAuth.js
import React from ‘react’;
import { useLocation, Navigate } from ‘react-router-dom’;

function RequireAuth({ children }) {
const isAuthenticated = / 你的认证逻辑,例如从 Context 或 Redux 获取 /;
const location = useLocation();

if (!isAuthenticated) {
// 如果未认证,重定向到登录页,并保存当前路径以便登录后返回
return ;
}

return children;
}

export default RequireAuth;

// src/pages/Login.js
import React, { useState } from ‘react’;
import { useNavigate, useLocation } from ‘react-router-dom’;

function Login() {
const [username, setUsername] = useState(”);
const [password, setPassword] = useState(”);
const navigate = useNavigate();
const location = useLocation();

const from = location.state?.from?.pathname || ‘/dashboard’; // 登录成功后要跳转的页面

const handleLogin = (e) => {
e.preventDefault();
// 假设认证成功
console.log(‘Logging in with:’, username, password);
const isAuthenticated = true; // 实际应为认证服务返回的结果

if (isAuthenticated) {
  // 模拟设置认证状态
  // 这里你需要将 isAuthenticated 状态提升到 App 或 Context 中
  // 例如:authContext.login();
  navigate(from, { replace: true }); // 登录成功后跳转
} else {
  alert('Login failed!');
}

};

return (

Login

setUsername(e.target.value)} />
setPassword(e.target.value)} />

);
}
export default Login;

// src/App.js (更新路由配置)
// …
import RequireAuth from ‘./components/RequireAuth’;
import Login from ‘./pages/Login’;

function App() {
// 假设 isAuthenticated 状态在这个地方 (或者通过 Context 提供)
const [isAuthenticated, setIsAuthenticated] = React.useState(false); // 模拟认证状态

// 提供认证上下文
const authContextValue = React.useMemo(() => ({
isAuthenticated,
login: () => setIsAuthenticated(true),
logout: () => setIsAuthenticated(false),
}), [isAuthenticated]);

return (

    <hr />

    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
      <Route path="/contact" element={<Contact />} />
      <Route path="/users/:id" element={<UserProfile />} />
      <Route path="/login" element={<Login />} />

      {/* 受保护的路由 */}
      <Route path="/dashboard" element={<RequireAuth><DashboardLayout /></RequireAuth>}>
        <Route index element={<DashboardOverview />} />
        <Route path="overview" element={<DashboardOverview />} />
        <Route path="settings" element={<DashboardSettings />} />
        <Route path="reports" element={<DashboardReports />} />
      </Route>

      <Route path="*" element={<NotFound />} />
    </Routes>
  </div>
</AuthContext.Provider>

);
}

// 模拟 AuthContext
export const AuthContext = React.createContext(null);

export default App;
``
在这个例子中,
RequireAuth组件检查用户是否认证。如果未认证,它会使用Navigate组件重定向到/login路径,并使用state` prop 保存用户最初尝试访问的路径,以便登录成功后可以返回。

总结

React Router v6 通过其更简洁的 API、改进的嵌套路由支持和新的 Hooks,极大地简化了 React 应用中的路由管理。掌握 BrowserRouterRoutesRouteLinkNavLink 以及 useNavigateuseParamsuseLocation 等核心概念和 Hooks,你将能够构建出高效、用户友好的单页应用。

始终记得将 BrowserRouter 包裹在应用的根部,将 Routes 组件用于定义路由集合,并根据需要利用其他组件和 Hooks 来实现复杂的导航和页面逻辑。通过实践和不断探索,你会发现 React Router v6 是构建健壮 React 应用的强大工具。


滚动至顶部