根据 react-router-dom 的版本,有不同的方式
一、react-router-dom v6.3
用到的主要 api:
BrowserRouter
useRoutes
Outlet
下面是详细步骤:
1、index.js
BrowserRouter
用来实现单页的客户端路由
- 使用 BrowserRouter
包裹 App
- 放在
顶级
位置,重要!! - 支持嵌套路由,用于
history
模式 hash
模式使用HashRouter
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { BrowserRouter } from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<BrowserRouter>
<React.StrictMode>
<App />
</React.StrictMode>
</BrowserRouter>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
如果遇到下图这种报错,就是没有将 BrowserRouter 像上图一样放在 顶级
,一定要放顶级!
2、routes.js
用 数组 描述路由,包含必要参数 path
、element
、children
例如:
import { UserOutlined } from "@ant-design/icons";
import Abroad from "./pages/Abroad";
import Domestic from "./pages/Domestic";
import Layout from "./layout/index";
import { Navigate } from "react-router-dom";
const routes = [
{
title: "平台管理",
path: "/manage",
element: <Layout />,
icon: <UserOutlined />,
children: [
{
title: "境内平台管理",
path: "/manage/domestic",
element: <Domestic />,
icon: <UserOutlined />,
},
{
title: "境外平台管理",
path: "/manage/abroad",
element: <Abroad />,
icon: <UserOutlined />,
},
],
},
{
path: "/",
element: <Navigate to="/manage/domestic" />,
},
];
export default routes;
3、App.js
使用 useRoutes
渲染出一级路由,参数即为我们在上一步的路由数组
- useRoutes 是
react-router-dom
库中提供的一个 Hook,用于基于路由配置生成路由匹配器
(Router Matcher)的方法 - useRoutes 接收的路由配置对象应该是一个由
若干个路由配置组成的数组
,每个路由配置包含以下属性:path
:字符串类型,表示 URL 路径匹配规则;element
:React 组件,表示如果 URL 匹配成功后要显示的组件;children
:嵌套子路由的路由配置数组。
import React from "react";
import { useRoutes } from "react-router-dom";
import routes from "./routes";
const App = () => {
const element = useRoutes(routes);
return <>{element}</>;
};
export default App;
4、layout.jsx
实际场景,如后台管理项目,需要展示多级路由,我们一般会创建一个layout文件
- 按照
左 Menu
,右 Content
的设定来举例 - 嵌套的二级路由,使用
outlet
渲染(类似于vue中的<router-view>
) - 例子中,包含了一些菜单处理的代码,如递归渲染、默认选中,可选择性观看
import React, { useState } from "react";
import { MenuFoldOutlined, MenuUnfoldOutlined } from "@ant-design/icons";
import { Layout, Menu, Button, theme } from "antd";
import { Outlet, useNavigate, useLocation } from "react-router-dom";
import routes from "../routes/index";
const { Header, Sider, Content } = Layout;
// Menu格式处理
function getMenu(rout) {
const rs = rout.map((item) => {
return {
label: item.title,
key: item.path,
icon: item.icon,
children: item.children && getMenu(item.children),
};
});
return rs;
}
// 递归层级
function findParents(key, tree) {
let parents = [];
function findParent(nodes, key) {
for (let node of nodes) {
if (node.path === key) {
return true;
}
if (node.children && findParent(node.children, key)) {
parents.push(node.path);
return true;
}
}
return false;
}
findParent(tree, key);
parents.push(key);
return parents;
}
// 获取当前路由所在层级
function getOpenKeys(pathname, list) {
if (!pathname) return ["/manage", "/manage/domestic"];
let keys = findParents(pathname, list);
return keys;
}
const App = () => {
const [collapsed, setCollapsed] = useState(false);
const {
token: { colorBgContainer, borderRadiusLG },
} = theme.useToken();
// 递归获取有效菜单项
const list = routes.filter((item) => item.title);
const items = getMenu(list);
// 菜单,默认展开、选中处理
const location = useLocation();
const keys = getOpenKeys(location.pathname, list);
const defaultSelectedKeys = keys;
const defaultOpenKeys = keys;
// 跳转
let navigate = useNavigate();
function handleClick(e) {
navigate(e.key);
}
return (
<Layout>
<Sider trigger={null} collapsible collapsed={collapsed}>
<div style={{ width: "100%", height: "80px" }}></div>
{/* 菜单 */}
<Menu
mode="inline"
theme="dark"
defaultSelectedKeys={defaultSelectedKeys}
defaultOpenKeys={defaultOpenKeys}
onClick={handleClick}
items={items}
></Menu>
</Sider>
<Layout>
<Header
style={{
padding: 0,
background: colorBgContainer,
}}
>
<Button
type="text"
onClick={() => setCollapsed(!collapsed)}
style={{ marginBottom: 16 }}
>
{collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
</Button>
</Header>
<Content
style={{
margin: "24px 16px",
padding: 24,
minHeight: 280,
background: colorBgContainer,
borderRadius: borderRadiusLG,
overflowY: "auto",
}}
>
{/* 二级路由 */}
<Outlet />
</Content>
</Layout>
</Layout>
);
};
export default App;
v6.3 的版本,路由设置到此结束,接下来是 v6.4
二、react-router-dom v6.4
主要 api:
createBrowserRouter
RouterProvider
Outlet
在 v6.3 方案的基础上,换掉 index.js 和 App.js 即可
1、index.js
把 BrowserRouter
的使用去掉
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
// v6.3 使用此API,v6.4使用会报错
// import { BrowserRouter } from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
// <BrowserRouter>
<React.StrictMode>
<App />
</React.StrictMode>
// </BrowserRouter>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
2、App.js
把 useRoutes
的使用去掉,增加 createBrowserRouter
、RouterProvider
的相关设置
createBrowserRouter
创建路由- 挂载到
RouterProvider
的router
上
import React from "react";
import routes from "./routes";
// v6.3 userRoutes钩子
// import { useRoutes } from "react-router-dom";
// const App = () => {
// const element = useRoutes(routes);
// return <>{element}</>;
// };
// v6.4 createBrowserRouter + RouterProvider
import { createBrowserRouter, RouterProvider } from "react-router-dom";
const router = createBrowserRouter(routes);
const App = () => {
return (
<>
<RouterProvider router={router} />
</>
);
};
export default App;
然后就完成啦~