九、React Router 6 快速上手

郁子大约 7 分钟约 2025 字笔记React16.8尚硅谷张天禹React Router 6

(一)概述

1.React Router 以三个不同的包发布到 npm 上

  • react-router
    • 路由的核心库,提供了很多组件、钩子
  • react-router-dom
    • 包含 react-router 所有内容,并添加一些专门用于 DOM 的组件
    • 例如 <BrowserRouter>
  • react-router-native
    • 包括 react-router 所有内容,并添加一些专门用于 React Native 的 API
    • 例如 <NativeRouter>

2.与 React Router 5.x 相比

  • 内置组件变化:移除 <Switch /> ,新增 <Routes />
  • 语法变化: component={Xxx} 变为 element={<Xxx/>}
  • 新增多个 hook: useParamsuseNavigateuseMatch
  • 官方明确推荐函数式组件
  • ......

(二)Component

1.<BrowserRouter>

  • 用于包裹整个应用
// 引入React核心库
import React from "react";
// 引入ReactDOM
import ReactDOM from "react-dom";
// 引入路由
import { BrowserRouter } from "react-router-dom";
// 引入App组件
import App from "./App";

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root"),
);

2.<HashRouter>

  • 作用与 <BrowserRouter> 一样,但 <HashRouter> 修改的是地址栏的 hash
  • v6.x 的 <BrowserRouter><HashRouter> 用法与 v5.x 相同
  • 修改 URL,且不发送网络请求(路由链接)
  • 外侧需要用 <BrowserRouter><HashRouter> 包裹
import { Link } from "react-router-dom";

function Test() {
  return (
    <div>
      <Link to="/路径">按钮</Link>
    </div>
  );
}
  • <Link> 组件类似,且可实现导航的高亮效果
  • NavLink 默认高亮类名是 active
  • 如果需要自定义高亮类名应使用函数返回字符串
<NavLink
  to="login"
  className={({ isActive }) => {
    console.log("home", isActive);
    return isActive ? "base one" : "base";
  }}
>
  Login
</NavLink>
/*
 * 1.默认情况下,当Home组件的子组件匹配成功,Home导航也会高亮。
 * 2.当NavLink上添加了end属性后,Home组件的子组件匹配成功时,Home导航不会高亮。
 */
<NavLink to="home" end>
  Home
</NavLink>

5.<Routes/><Route/>

  • v6 版本中移除了先前的 <Switch> ,引入了新的替代者: <Routes>
  • <Routes><Route> 要配合使用,且必须用 <Routes> 包裹 <Route>
  • <Route> 相当于一个 if 语句,如果其路径与当前 URL 匹配,则呈现其对应的组件
  • <Route caseSensitive> 属性用于指定:匹配时是否区分大小写(默认为 false
  • 当 URL 发生变化时, <Routes> 都会查看其所有子 <Route> 元素,以找到最佳匹配路由组件并呈现
  • <Route> 也可以嵌套使用,且可配合 useRoutes() 配置“路由表”,但需要通过 <Outlet> 组件来渲染其子路由
<Routes>
  {/* path属性用于定义路径,element属性用于定义当前路径所对应的组件 */}
  <Route path="/login" element={<Login />}></Route>

  {/* Route嵌套用于定义嵌套路由,home是一级路由,对应的路径为/home */}
  <Route path="home" element={<Home />}>
    {/* test1、test2是二级路由,对应的路径为/home/test1、/home/test2 */}
    <Route path="test1" element={<Test1 />}></Route>
    <Route path="test2" element={<Test2 />}></Route>
  </Route>

  {/* Route也可以不写element属性,这时就是用于展示嵌套的路由,所对应的路径是/users/xxx */}
  <Route path="users">
    <Route path="xxx" element={<Xxx />} />
  </Route>
</Routes>

6.<Navigate>

  • 只要 <Navigate> 组件被渲染,就会修改路径,切换视图
  • replace 属性用于控制跳转模式( pushreplace ,默认是 push
import React, { useState } from "react";
import { Navigate } from "react-router-dom";

export default function Home() {
  const [sum, setSum] = useState(1);
  return (
    <div>
      <h3>我是Home的内容</h3>
      {sum === 2 ? <Navigate to="/about" replace /> : <h4>当前sum值是:{sum}</h4>}
      <button onClick={() => setSum(2)}>点我将sum变为2</button>
    </div>
  );
}

7.<Outlet>

  • <Route> 产生嵌套时,渲染其对应的后续子路由
// /src/pages/Home.jsx
import React from "react";
import { NavLink, Outlet } from "react-router-dom";

export default function Home() {
  return (
    <div>
      <h2>Home组件内容</h2>
      <div>
        <ul className="nav nav-tabs">
          <li>
            {/* /home/news <=> ./news <=> news */}
            <NavLink className="list-group-item" to="news">
              News
            </NavLink>
          </li>
          <li>
            <NavLink className="list-group-item" to="message">
              Message
            </NavLink>
          </li>
        </ul>
        {/* 指定路由组件呈现的位置 */}
        <Outlet />
      </div>
    </div>
  );
}
// /src/routes/index.js
import { Navigate } from "react-router-dom";
import About from "../pages/About";
import Home from "../pages/Home";

const routes = [
  {
    path: "/about",
    element: <About />,
  },
  {
    path: "/home",
    element: <Home />,
    children: [
      {
        path: "news",
        element: <News />,
      },
      {
        path: "message",
        element: <Message />,
      },
    ],
  },
  {
    path: "/",
    element: <Navigate to="/about" />,
  },
];
export default routes;

(三)Hooks

1.useRoutes()

  • 根据路由表,动态创建 <Routes><Route>
// /src/App.jsx
import routes from "./routes";

export default function App() {
  // 根据路由表生成对应的路由规则
  const element = useRoutes(routes);
  return (
    <div className="panel-body">
      {/* 注册路由 */}
      {element}
    </div>
  );
}

2.useNavigate()

  • 返回一个函数用来实现编程式导航
import React from "react";
import { useNavigate } from "react-router-dom";

export default function Demo() {
  const navigate = useNavigate();
  const handle = () => {
    // 方式一:指定具体的路径,仅支持传递state属性,params和search参数直接拼在第一个参数(路径)后面
    navigate("/login", {
      replace: false,
      state: {
        a: 1,
        b: 2,
      },
    });
    // 方式二:传入数值进行前进或后退,类似于v5.x中的history.go()方法
    navigate(-1);
  };

  return (
    <div>
      <button onClick={handle}>按钮</button>
    </div>
  );
}

3.useParams()

  • 返回当前匹配路由的 params 参数
  • 类似于 v5.x 中的 match.params

1)父组件

import React, { useState } from "react";
import { Link, Outlet } from "react-router-dom";

export default function Message() {
  const [messages] = useState([
    { id: "001", title: "消息1", content: "锄禾日当午" },
    { id: "002", title: "消息2", content: "汗滴禾下土" },
    { id: "003", title: "消息3", content: "谁知盘中餐" },
    { id: "004", title: "消息4", content: "粒粒皆辛苦" },
  ]);
  return (
    <div>
      <ul>
        {messages.map((m) => {
          return (
            // 路由链接
            <li key={m.id}>
              <Link to={`detail/${m.id}/${m.title}/${m.content}`}>{m.title}</Link>
            </li>
          );
        })}
      </ul>
      <hr />
      {/* 指定路由组件的展示位置 */}
      <Outlet />
    </div>
  );
}

2)子组件

import React from "react";
import { useParams } from "react-router-dom";

export default function Detail() {
  const { id, title, content } = useParams();
  return (
    <div>
      <ul>
        <li>消息编号:{id}</li>
        <li>消息标题:{title}</li>
        <li>消息内容:{content}</li>
      </ul>
    </div>
  );
}

4.useSearchParams()

  • 用于读取和修改当前位置的 URL 中的查询字符串
  • 返回一个包含两个值的数组:当前的 search 参数、更新 search 参数的函数

1)父组件

import React, { useState } from "react";
import { Link, Outlet } from "react-router-dom";

export default function Message() {
  const [messages] = useState([
    { id: "001", title: "消息1", content: "锄禾日当午" },
    { id: "002", title: "消息2", content: "汗滴禾下土" },
    { id: "003", title: "消息3", content: "谁知盘中餐" },
    { id: "004", title: "消息4", content: "粒粒皆辛苦" },
  ]);
  return (
    <div>
      <ul>
        {messages.map((m) => {
          return (
            // 路由链接
            <li key={m.id}>
              <Link to={`detail?id=${m.id}&title=${m.title}&content=${m.content}`}>{m.title}</Link>
            </li>
          );
        })}
      </ul>
      <hr />
      {/* 指定路由组件的展示位置 */}
      <Outlet />
    </div>
  );
}

2)子组件

import React from "react";
import { useSearchParams } from "react-router-dom";

export default function Detail() {
  const [search, setSearch] = useSearchParams();
  const id = search.get("id");
  const title = search.get("title");
  const content = search.get("content");

  return (
    <div>
      <ul>
        <li>
          {/* 了解即可 */}
          <button
            onClick={() => {
              setSearch("id=008&title=哈哈&content=嘻嘻");
            }}
          >
            点我更新一下收到的search参数
          </button>
        </li>
        <li>消息编号:{id}</li>
        <li>消息标题:{title}</li>
        <li>消息内容:{content}</li>
      </ul>
    </div>
  );
}

5.useLocation()

  • 获取当前 location 信息,对标 v5.x 中的路由组件的 location 属性
import React from "react";
import { useLocation } from "react-router-dom";

export default function Detail() {
  const x = useLocation();
  console.log(x);
  /*
  	x就是location对象:
    {
      hash: "",
      key: "ah9nv6sz",
      pathname: "/login",
      search: "?name=zs&age=18",
      state: {a: 1, b: 2}
    }
  */

  return (
    <div>
      <ul>
        <li>消息编号:{id}</li>
        <li>消息标题:{title}</li>
        <li>消息内容:{content}</li>
      </ul>
    </div>
  );
}

6.useMatch()

  • 返回当前匹配信息,对标 v5.x 中的路由组件的 match 属性
import React from "react";
import { useParams, useMatch } from "react-router-dom";

export default function Detail() {
  // 了解即可
  console.log(useMatch("/home/message/detail/:id/:title/:content"));
  /*
  	x就是location对象:
    {
      params: {x: '1', y: '10'},
      pathname: "/xxxx/xxx/xx",
      pathnameBase: "/xxxx/xxx/xx",
      pattern: {
        path: "/xxxx/:x/:y",
        caseSensitive: false,
        end: false
      }
    }
  */

  const { id, title, content } = useParams();
  return (
    <div>
      <ul>
        <li>消息编号:{id}</li>
        <li>消息标题:{title}</li>
        <li>消息内容:{content}</li>
      </ul>
    </div>
  );
}

7.useInRouterContext()

  • 如果组件在 <Router> 的上下文中呈现,则该钩子返回 true ,否则返回 false

8.useNavigationType()

  • 返回当前的导航类型(用户是如何来到当前页面的)
  • 返回值: POPPUSHREPLACE
  • POP 是指在浏览器中直接打开了当前路由组件(刷新页面)

9.useOutlet()

  • 用来呈现当前组件中要渲染的嵌套路由
  • 如果嵌套路由没有挂载,则返回 null
  • 如果嵌套路由已经挂载,则展示嵌套的路由对象
import React from "react";
import { NavLink, Outlet, useOutlet } from "react-router-dom";

export default function Home() {
  console.log(useOutlet());
  return (
    <div>
      <h2>Home组件内容</h2>
      <div>
        <ul className="nav nav-tabs">
          <li>
            {/* /home/news <=> ./news <=> news */}
            <NavLink className="list-group-item" to="news">
              News
            </NavLink>
          </li>
          <li>
            <NavLink className="list-group-item" to="message">
              Message
            </NavLink>
          </li>
        </ul>
        {/* 指定路由组件呈现的位置 */}
        <Outlet />
      </div>
    </div>
  );
}

10.useResolvedPath()

  • 给定一个 URL 值,解析其中的: pathsearchhash
上次编辑于: