九、React Router 6 快速上手
大约 7 分钟约 2025 字
(一)概述
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:
useParams
、useNavigate
、useMatch
等 - 官方明确推荐函数式组件
- ......
(二)Component
<BrowserRouter>
1.- 用于包裹整个应用
// 引入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"),
);
<HashRouter>
2.- 作用与
<BrowserRouter>
一样,但<HashRouter>
修改的是地址栏的hash
值 - v6.x 的
<BrowserRouter>
、<HashRouter>
用法与 v5.x 相同
<Link>
3.- 修改 URL,且不发送网络请求(路由链接)
- 外侧需要用
<BrowserRouter>
或<HashRouter>
包裹
import { Link } from "react-router-dom";
function Test() {
return (
<div>
<Link to="/路径">按钮</Link>
</div>
);
}
<NavLink>
4.- 与
<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>
<Routes/>
与 <Route/>
5.- 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>
<Navigate>
6.- 只要
<Navigate>
组件被渲染,就会修改路径,切换视图 replace
属性用于控制跳转模式(push
或replace
,默认是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>
);
}
<Outlet>
7.- 当
<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()
- 返回当前的导航类型(用户是如何来到当前页面的)
- 返回值:
POP
、PUSH
、REPLACE
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 值,解析其中的:
path
、search
、hash
值