react 添加动态路由

react 添加动态路由

需求


在产品创建初期,有菜单权限的要求,需要进行菜单鉴权,这时需要登录后向后端请求此用户存在哪些菜单,前端根据后端返回的菜单组件||文件的地址去注册路由及菜单栏的展示。

​ 如:点击菜单栏首页,界面跳转首页界面

实现


版本

react: "^18.1.0" react-router-dom: "^6.3.0" antd: "^4.20.5"

步骤

一、在App.js中引入所需文件,并编写引入登录页

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React from 'react'
import {BrowserRouter,Routes,Route} from 'react-router-dom'
import loadable from './utils/loadable' //组件加载进度条

// 公共模块,也就是首页
const DefaultLayout = loadable(() => import(/* webpackChunkName: 'default' */ './layout'))

// 登录页面
const Login = loadable(() => import(/* webpackChunkName: 'login' */ './views/Login'))

// 路由组件
function App() {
return (
<BrowserRouter>
<Routes>
<Route path='/login' element={<Login></Login>} />
<Route path='/*' element={<DefaultLayout></DefaultLayout>} />
</Routes>
</BrowserRouter>
);
}

export default App;

二、在首页layout页面添加如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import React, { Component } from 'react'

// eslint-disable-next-line no-unused-vars
import {Navigate, Route, Routes,useNavigate} from "react-router-dom"
import { Layout,message } from 'antd'

import '../style/layout.less' //页面样式

import AppHeader from './AppHeader' //首页头部
import AppAside from './AppAside.jsx' //首页侧边栏

import loadable from '../utils/loadable' //页面顶部加载进度条

const { Header, Content } = Layout; // 引入页面布局组件

const View404 = loadable(() => import('../views/Others/404'))// 404页面

class DefaultLayout extends Component {
state = {
collapsed: false,
menus: []
}

// 组件token判断及加载完成后获取菜单数据
componentDidMount() {
const token = sessionStorage.getItem('access_token')
if(!token){
const { navigation } = this.props;
navigation('/login')
}
const menus = sessionStorage.getItem('menus')
if(menus){
this.setState({
menus: JSON.parse(menus)
})
}
}

render() {
// 获取循环返回路由组件
const getComponent = (item) => {
const Com = loadable(() => import('../views'+item.pathKey))
return <Com />
}

return (
<Layout className="app">
<AppAside collapsed={this.state.collapsed}/>
<Layout>
<Header className="layout-header-box">
<AppHeader loginOut={loginOut} collapsed={this.state.collapsed} collapsedChange={()=>{
this.setState({
collapsed: !this.state.collapsed
})
}}/>
</Header>
<Content className="layout-content-box">
<Routes>
{/* dictionaryTable,为侧边栏菜单的第一个,这边写死的,后面通过后端返回的进行遍历取第一个放进来进行重定向 */}
<Route path='/' element={<Navigate to={'/dictionaryTable'}/>}/>
{/* 循环注册路由 */}
{this.state.menus.map((item,index) => {
return (
<Route key={index} path={item.url} element={getComponent(item)} />
)
})}
{/* 没找到进行404界面 */}
<Route path='*' element={<View404/>}/>
</Routes>
</Content>
</Layout>
</Layout>
)
}
}

// eslint-disable-next-line import/no-anonymous-default-export
export default function(props) {
const navigation = useNavigate();
return <DefaultLayout {...props} navigation={navigation} />;
}

三、引入在首页的侧边栏组件如下(菜单数据暂时写死,后面进行优化通过后端返回的数据进行展示)

​ 主要查看菜单点击事件的onClick()中进行路由跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import React from 'react'
import { Menu, Layout } from 'antd'
import { AppstoreOutlined, MailOutlined, SettingOutlined } from '@ant-design/icons';
import { useNavigate,useLocation } from 'react-router-dom'

import '../style/layout.less';

const { Sider } = Layout;

// 菜单数据
function getItem(label, key, icon, children, type) {
const item = {key,icon,children,label,type,}
return item;
}

// 菜单数据
const items = [
getItem('字典表', '/dictionaryTable', <MailOutlined />),
getItem('工单管理', '/order', <AppstoreOutlined />, [
getItem('工单列表', '/order/orderList'),
getItem('工单统计', '/order/orderStatistics')
]),
getItem('系统设置', '/systemSetting', <AppstoreOutlined />, [
getItem('菜单设置', '/menuControl')
]),
getItem('资产管理', '/assetManagement', <SettingOutlined />),
getItem('区域管理', '/regionalManagement', <SettingOutlined />,)
];

const AppAside = (props) => {

const localhost = useLocation(); // 获取当前路由,用于设置菜单选中状态

const openKeys = localhost.pathname.split('/')[1]; // 获取当前路由的第一个字符串

const onClick = (e) => {
navigation(e.key)
};

const navigation = useNavigate();

return (
<Sider width={250} className="layout-sider-box" collapsed={props.collapsed}>
{props.collapsed?
<div className="sider-logo-box vertical-horizontal-center f20">Logo</div>
:
<div className="sider-logo-box vertical-horizontal-center f30">Logo</div>}
<Menu
onClick={onClick}
defaultSelectedKeys={['/dictionaryTable']}
defaultOpenKeys={['/' + openKeys]}
mode="inline"
theme="dark"
items={items}
/>
</Sider>
)
}

// eslint-disable-next-line import/no-anonymous-default-export
export default (props) => {
return <AppAside {...props}/>;
}