React07-路由管理器react-router-dom(v6)

react-router 是一个流行的用于 React 应用程序路由的库。它使我们能够轻松定义应用程序的路由,并将它们映射到特定的组件,这样可以很容易地创建复杂的单页面应用,并管理应用程序的不同视图。

react-router 是基于 React 构建的,因此与其他 React 库和工具集成得很好。它在许多 React 应用程序中广泛使用,并被认为是 React 中最佳实践的路由。

一、react-router-dom安装和简介

1. react-router-dom安装

使用 npm 或 yarn 安装 react-router-dom。

npm install --save react-router-dom
yarn add react-router-dom

2. react-router-dom 简介

react-router-dom 是 react-router 的一种实现方案,主要应用于网页端应用,它提供了一些常用的组件进行路由管理。

(1) Router 类组件

  • BrowserRouter:history 模式下的路由。
  • HashRouter:hash 模式下的路由。

(2) Route 组件

Route 是一个路由配置组件。

(3) Link 组件

Link 类似于 a 标签,可以用于路由跳转。

(4) useNavigate Hook

useNavigate 是一个钩子函数,可以用于路由跳转。

二、react-router-dom 基本使用

1. 引入 react-router-dom

javascript 复制代码
import {BrowserRouter as Router, Routes, Route} from 'react-router-dom';

这里的 BrowserRouter as Router 相当于给 BrowserRouter 起了一个别名叫 Router。Routes 组件用于包裹 Route 组件,Route 组件的父组件必须是 Routes。

2. 简单使用

使用 BrowserRouter 组件包裹 Routes 组件,再用 Routes 组件包裹 Route 组件,在 Route 组件中定义路由信息,就实现了一个简单的 react-router 场景。

Route 组件有 path 和 element 两个属性,path 属性代表路由路径,element 属性代表要渲染的组件。

index.js

javascript 复制代码
import ReactDOM from 'react-dom/client';
import App from './App';
import {BrowserRouter as Router} from 'react-router-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Router>
    <App />
  </Router>
);

app.js

javascript 复制代码
import './App.css';
import { Routes, Route} from "react-router-dom";
import Home from "./views/Home";
import About from "./views/About";
import Error from "./views/Error";

function App() {
  return (
    <div className="App">
      <Routes>
        <Route path="/" element={<Home/>}/>
        <Route path="/about" element={<About/>}/>
        <Route path="*" element={<Error/>}/>
      </Routes>
    </div>
  );
}

export default App;

在上面的案例中,我们定义了一个根路由,指向 Home 页面。还定义一个指定路径路由 /about,指向 About 页面,表示当我们在浏览器地址栏中的页面地址后加上 /about 时,会跳转到 About 页面。

又定义了一个通用路由,指向 Error 页面,path="*" 表示如果浏览器地址栏中的页面地址不是以上定义的2种路由(即其他路由地址)时,就会跳转到 Error 页面。

注意:BrowserRouter 组件最好放在最顶层所有组件之外,这样能确保内部组件使用 Link 做路由跳转时不出错。

3. 路由跳转

跳转路由时,如果路由路径是 / 开头的是绝对路由,否则是相对路由,即相对于当前 URL进行改变。

Link 组件只能在 Router 组件的内部使用,to 属性代表要跳转的路由地址。

javascript 复制代码
import { Link } from "react-router-dom";
javascript 复制代码
<Link to="/about">To About</Link>

NavLink 组件和 Link 组件的功能是一致的,区别在于 NavLink 组件可以判断其 to 属性是否是当前匹配到的路由。

NavLink组件的 style 属性或 className 属性可以接收一个函数,函数接收一个含有 isActive 字段的对象为参数,可根据该参数调整样式。

javascript 复制代码
import { Link } from "react-router-dom";
javascript 复制代码
<NavLink to="/about" style={({ isActive }) => ( {color: isActive ? "red" : "black"} )}>To About</NavLink>

(3) useNavigate Hook

使用 useNavigate 钩子函数,可以在 js 代码中实现路由跳转。

useNavigate 必须在 Route 的上下文中使用,即必须包裹在 Router 标签内。

javascript 复制代码
import { useNavigate } from "react-router-dom";
javascript 复制代码
<button onClick={() => navigate("/about")}>To About</button>

4. 路由参数

(1) 路径参数(动态路由匹配)

在 Routes 组件内定义路由。对于动态路由,使用冒号(:)来指定动态部分。例如,要定义一个动态用户路由,可以:

App.js

javascript 复制代码
<Routes>
  <Route path="/user/:userId" element={<User />} />
</Routes>

这里的 :userId 是动态部分,可以匹配任何放在 /user/ 后面的路径,比如 /user/123。

在路由的目标组件中,可以使用 useParams 钩子来访问动态参数。例如,在 User 组件中,可以这样获取 userId:

User.js

javascript 复制代码
mport { useParams } from 'react-router-dom';

function User {
  let { userId } = useParams();
  console.log(userId); // 123
  // 使用 userId 作为你的逻辑的一部分
}

export default User;

上述案例中,访问 /user/123 能正确获取到 userId 为123。

(2) 查询参数

查询参数不需要在路由中定义,可以使用 useSearchParams 钩子函数来处理路径中的查询参数。

查询参数位于 URL 中,用于提供额外信息以便服务器能够更具体地处理请求。它们通常出现在网页地址(URL)的末尾,并且遵循一定的格式。

在 URL 中,查询参数是通过问号(?)与网址的其它部分分隔开的,并且可以包含一个或多个键值对。每个键值对都由一个键(key)和一个值(value)组成,用等号(=)连接。如果有多个查询参数,它们之间通常用符号与(&)分隔。

例如,在 URL https://example.com/page?param1=value1&param2=value2 中, param1=value1 和 param2=value2 是查询参数,它们提供了额外的信息给服务器。

useSearchParams Hook 返回当前组件获取到的查询参数对象及修改这个对象的方法,使用方法和 useState Hook 类似。

在使用 setSearchParams 方法时,必须传入所有查询参数,否则会覆盖已有查询参数。

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

function User() {
  // 使用 useSearchParams 钩子
  const [searchParams, setSearchParams] = useSearchParams();

  // 获取查询参数
  const userId = searchParams.get('userId');

  // 设置查询参数
  const setUserId = (newUserId) => {
    setSearchParams({ userId: newUserId });
  };

  // 渲染组件
  return (
    <div>
      <p>当前的 userId 是:{userId}</p>
      <button onClick={() => setUserId('123')}>设置 userId 为 123</button>
    </div>
  );
}

export default User;

上述案例中,访问 /user?userId=123456 时,效果如下:

点击按钮后,userId 变为 123,符合预期。

(3) 状态参数

react-router 对 window.location 进行了包装,状态参数就位于包装后的 location 对象的 state 属性中。下面为包装后的 location 对象的属性:

|----------|-----------------------------|
| pathname | 主机名之后的 URL 地址 |
| search | 查询参数 |
| hash | 哈希值,用于确定页面滚动的具体位置 |
| state | 对于 window.history.state 的包装 |
| key | 每个 location 对象拥有一个唯一的 key |
[react-router包装后的location对象]

state 不显示在页面上,不会引起页面刷新,可用于记录用户的跳转详情或在跳转时携带信息,可以用在 Link 组件或 navigate 方法中使用状态参数。

javascript 复制代码
{/* Link 中使用状态参数 */}
<Link to="/about" state={{ id: 123 }} >To About</Link>

{/* useNavigate 中使用状态参数 */}
<button onClick={() => navigate("/about", { state: { id: 456 } })}>To About</button>

在目标组件中,使用 useLocation 来获取封装后的 loaction 对象,进而获取状态参数。

javascript 复制代码
const location = useLocation();
console.log(location.state);

5. 路由重定向

当在某个路径 /a ,要重定向到路径 /b 时,可以通过 Navigate 组件进行重定向到其他路径。

javascript 复制代码
import { Navigate } from "react-router-dom";

function A() {
  return <Navigate to="/b" />;
}

export default A;

三、react-router-dom 进阶使用

1. 嵌套路由

嵌套路由是前端开发中用于组织和管理路由的一种技术,特别适用于构建具有层次结构的页面和应用程序。

在 Route 组件内定义 Route 组件实现嵌套路由,在父组件中使用 Outlet 组件作为子组件的占位符(显示匹配到的子组件)。

在嵌套路由中,如果 URL 仅匹配了父级 URL,则 Outlet 组件中会显示带有 index 属性的子路由,这个子路由称为默认路由。

例3-1:根据用户 id 显示用户详细信息

App.js

javascript 复制代码
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import Home from './views/Home';
import Users from './views/Users';
import UserList from "./views/UserList";
import UserDetail from "./views/UserDetail";

function App() {
  return (
    <Router>
      <nav>
        <Link to="/">Home</Link> |
        <Link to="/users">User</Link>
      </nav>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="users" element={<Users />}>
          <Route index element={<UserList />} />
          <Route path=":userId" element={<UserDetail />} />
        </Route>
      </Routes>
    </Router>
  );
}

export default App;

Users.js

javascript 复制代码
import { Link, Outlet } from "react-router-dom";

function Users() {
  return (
    <div>
      <h2>Users</h2>
      <nav>
        <Link to="list">User List</Link>
      </nav>
      <Outlet />
    </div>
  );
}

export default Users;

UserList.js

javascript 复制代码
import { Link } from "react-router-dom";

function UserList() {
  const users = [
    {id: "1", name: 'John'},
    {id: "2", name: 'Jane'},
    {id: "3", name: 'Mary'},
  ];
  return (
    <div>
      <h2>User List</h2>
      <ul>
        {users.map((user) => (
          <li key={user.id}>
            <Link to={`/users/${user.id}`}>{user.name}</Link>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default UserList;

UserDetail.js

javascript 复制代码
import { useParams } from 'react-router-dom';

function UserDetail() {
  const { userId } = useParams();
  const users = [
    {id: "1", name: 'John'},
    {id: "2", name: 'Jane'},
    {id: "3", name: 'Mary'},
  ];
  return (
    <div>
      <h3>User Detail</h3>
      <h3>id:{userId}</h3>
      <h3>name:{users.find((user) => user.id === userId).name}</h3>
    </div>
  )
}

export default UserDetail;

上述案例中,在根组件 App 中定义了一组嵌套路由,一级路由为 / 和 users。又在 users 中嵌套了默认路由和动态路由 :userid。

在这个嵌套路由中,Users 组件为父组件,UserList 和 UserDetail 组件为子组件。在 Users 组件中使用了 Outlet 组件,作为匹配到的子组件的占位符。可以使用在路由的任何层级。当访问 /users 时,这里触发默认路由显示 UserList 组件。当访问 /users/2 时,会显示 UserDetail 组件。

访问 /users:

访问 /users/2:

2. 多组路由

多组路由指的是在单个应用中设置多个独立的路由组,可以在不同部分的应用中实现独立的路由逻辑,增加了路由结构的灵活性和模块化。

在 react-router-dom 中,可以通过在不同的组件中定义多个 Routes 组件来实现多组路由。

例3-2 管理系统案例

App.js

javascript 复制代码
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import MainSite from './views/MainSite';
import AdminPanel from './views/AdminPanel';

function App() {
  return (
    <Router>
      <Routes>
        <Route path="*" element={<MainSite />} />
        <Route path="admin/*" element={<AdminPanel />} />
      </Routes>
    </Router>
  );
}

export default App;

MainSite.js

javascript 复制代码
import { Routes, Route, Link } from 'react-router-dom';
import Home from './Home';
import About from './About';

function MainSite() {
  return (
    <div>
      <nav>
        <Link to="/">Home</Link> |
        <Link to="/about">About</Link>
      </nav>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="about" element={<About />} />
      </Routes>
    </div>
  );
}

export default MainSite;

AdminPanel.js

javascript 复制代码
import { Routes, Route, Link } from 'react-router-dom';
import Dashboard from './Dashboard';
import Users from './Users';

function AdminPanel() {
  return (
    <div>
      <nav>
        <Link to="/admin/dashboard">Dashboard</Link> |
        <Link to="/admin/users">Users</Link>
      </nav>
      <Routes>
        <Route path="dashboard" element={<Dashboard />} />
        <Route path="users" element={<Users />} />
      </Routes>
    </div>
  );
}

export default AdminPanel;

上述案例构建了一个具有两个独立路由组的应用,这两个路由组分别是主站(MainSite 组件)和管理面板(AdminPanel 组件)。MainSite 组件包含了应用的主要页面,如首页(Home)和关于页面(About);AdminPanel 组件是应用的管理面板,包含了仪表盘(Dashboard)和用户管理(Users) 界面。

App.js 中定义了应用的顶级路由结构,使用 Routes 来定义两个主要路由路径:* 和 /admin/*。每个路径指向对应的 MainSite 和 AdminPanel 组件。* 路径指向 MainSite 组件,该组件处理与主站相关的子路由。/admin/* 路径指向 AdminPanel 组件,该组件处理与管理面板相关的子路由。

当访问 / 时,展示 MainSite 组件和 Home 组件;访问 /about,展示 MainSite 组件和 About 组件;访问 /admin,展示 AdminPanel 组件;访问 /admin/dashboard,展示 AdminPanel 组件和 Dashboard 组件。

3. 布局路由

布局路由是一种组织和管理路由的方式,它允许开发者将路由与其对应的布局分开管理。这在复杂的应用中特别有用,因为它可以帮助保持代码的清晰和模块化。

布局路由是指一种模式,其中一组路由共享相同的布局组件。这意味着这些路由的所有页面都将有相同的外观和感觉,例如共享的头部和底部导航栏。布局组件负责渲染这些共享元素,而特定的子路由则负责渲染页面的主要内容。

例3-3 升级版管理系统案例

App.js

同案例3-2。

MainSite.js

javascript 复制代码
import React from 'react';
import {Routes, Route, Link} from 'react-router-dom';
import Home from './Home';
import About from './About';
import Header from './Header';
import Footer from './Footer';
import Error from "./Error";

function MainSite() {
  return (
    <div>
      <Header />
      <nav>
        <Link to="/">Home</Link> |
        <Link to="/about">About</Link>
      </nav>
      <Routes>
        <Route index element={<Home />} />
        <Route path="about" element={<About />} />
        {/* 更多主站的路由可以在这里添加 */}

        {/* 捕获所有未匹配的路由并显示 Error 组件 */}
        <Route path="*" element={<Error />} />
      </Routes>
      <Footer />
    </div>
  );
}

export default MainSite;

AdminPanel.js

javascript 复制代码
import React from 'react';
import {Routes, Route, Link} from 'react-router-dom';
import Dashboard from './Dashboard';
import Users from "./Users";
import AdminHeader from './AdminHeader';
import AdminFooter from './AdminFooter';
import Error from "./Error";

function AdminPanel() {
  return (
    <div>
      <AdminHeader />
      <nav>
        <Link to="/admin">Dashboard</Link> |
        <Link to="/admin/users">Users</Link>
      </nav>
      <Routes>
        <Route path="" element={<Dashboard />} />
        <Route path="users" element={<Users />} />
        {/* 更多管理面板的路由可以在这里添加 */}
        {/* 捕获所有未匹配的路由并显示 Error 组件 */}
        <Route path="*" element={<Error />} />
      </Routes>
      <AdminFooter />
    </div>
  );
}

export default AdminPanel;

上述案例中,我们在 MainSite 和 AdminPanel 中加入了 Header 和 Footer 布局组件,同时添加了一个用于处理所有未匹配路由的 Error 组件。

相关推荐
崔庆才丨静觅10 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606111 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了11 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅11 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅11 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅12 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment12 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅12 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊12 小时前
jwt介绍
前端
爱敲代码的小鱼12 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax