前言
闲来无事,决定用react写一下后端返回路由表,生成路由,写的时候还是有挺多坑的,首先我用springboot写的后端接口来返回路由表的数据mysql做数据库毕竟本人不是专业写后端的就是实现了一些基本的功能例如跨域、mybatisplus、请求拦截、swagger、jwt、代码生成器、异常处理,用这些实现了一些基本的增删改查,至于前端是用的vite react react router v6,至于vite确实比webpack要快很多,不过vite在使用的过程中也是踩过一些坑的,毕竟接触新的东西都是要踩坑的,踩的坑多了也就会了。
后端代码我就不详细介绍了
一、首先用vite构建react项目目录结构如下
二、项目启动及eslint使用
三、主要代码展示
http.js
import axios from "axios";
const http = axios.create({
timeout: 1000 * 10,
baseURL: 'http://localhost:8080',
responseType: 'json',
});
http.interceptors.request.use(function (config) {
if (config.url !== '/user/login') {
config.headers.token = localStorage.getItem("token");
}
return config;
}, function (error) {
return Promise.reject(error);
});
http.interceptors.response.use(function (response) {
if (response.data.code == 200) {
return response.data;
} else if (response.data.code == 401) {
window.location.href = "http://localhost:5173/login";
return response.data;
}
}, function (error) {
return Promise.reject(error);
});
export default http;
router/index.jsx
import { useRoutes, useNavigate, useLocation } from 'react-router-dom';
import Redirect from './redirect';
import { useCallback, useEffect, useState } from 'react'
import Layout from '../layout/index';
import LazyLoad from './LazyLoad';
import http from '../util/http'
export default function IndexRouter() {
const location = useLocation();
const navigate = useNavigate();
const [backRouteList, setBackRounteList] = useState([])
const [notFound, setNotFound] = useState([]);
//URL 和Routes 组件之间的映射关系
const LocalRouterMap = useCallback((list) => {
const obj = {};
for (let i = 0; i < list.length; i++) {
if (list[i].element != '') {
obj[list[i].path] = LazyLoad(`${list[i].element}`)
}
}
return obj;
}, [])
//定义路由结构
const obj = (path, element) => {
return {
path,
element
}
}
//返回路由数据
const handle = useCallback((list, routerMapObj) => {
const arr = [];
list.map(item => {
arr.push(obj(item.path, routerMapObj[item.path]))
})
return arr;
}, []);
//扁平化数据结构
const recursion = useCallback((list) => {
const arr = [];
list.map(item => {
if (item?.children) {
item.children.map(item => {
arr.push(item)
})
}
arr.push(item)
})
return arr
}, [])
//请求后端路由数据
const getData = useCallback(async () => {
const res = await http.get(`/menu`)
localStorage.setItem('menu',JSON.stringify(res.data))
const arr = recursion(res.data); //返回扁平化的数据结构
const routerMapObj = LocalRouterMap(arr); //返回url和组件的映射结构对象
setBackRounteList(handle(arr, routerMapObj)) //生成路由数据
setNotFound([{
path: '*',
element: LazyLoad('notFound')
}])
}, [recursion, LocalRouterMap, handle])
useEffect(() => {
const whiteRoute = ['login']
if (whiteRoute.includes(location.pathname.split('/')[1])) {
navigate(location.pathname)
} else {
if (localStorage.getItem('token')) {
// 请求后端返回的路由数据
getData()
} else {
navigate('/login')
}
}
}, [getData, location.pathname, navigate])
const element = useRoutes(
[
{
path: '/login',
element: LazyLoad('login')
},
{
path: '/',
element: <Layout />,
children: [
{
path: '/',
element: <Redirect to="/home" />
},
...backRouteList
]
},
...notFound
]
)
return (element)
}
LazyLoad.jsx
//路由懒加载的封装
import React from 'react'
const LazyLoad = (path) => {
const Comp = React.lazy(() => import(`../views/${path}.jsx`))
return (
<React.Suspense fallback={<>加载中....</>}>
<Comp />
</React.Suspense>
)
}
export default LazyLoad
Redirect.jsx
import { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
export default function Redirect(props) {
const navigate = useNavigate()
useEffect(() => {
navigate(props.to, { replace: true })
}, [navigate,props.to])
return null
}
layout/index.jsx
import { Layout } from 'antd';
import SideMenu from './component/SideMenu'
import TopHeader from './component/TopHeader'
export default function index() {
return (
<Layout style={{ height: '100vh' }}>
<SideMenu></SideMenu>
<TopHeader></TopHeader>
</Layout>
)
}
layout/component/SideMenu.jsx
import style from './SideMenu.module.css'
import { useLocation,useNavigate } from 'react-router-dom'
import { Layout, Menu } from 'antd';
const { Sider } = Layout;
export default function SideMenu() {
const navigate = useNavigate();
const location = useLocation();
const selectedKeys = [location.pathname]
const openKeys = ['/' + location.pathname.split('/')[1]]
const obj = (key, label, icon,children) => {
return {
key,
label,
icon,
children,
}
}
const handleMenu = (menu) => {
const arr = [];
menu.map(item => {
if (item.children && item.children.length !== 0) { //判断有children
arr.push(obj(item.path, item.lable, item.icon,handleMenu(item.children)))
} else { //判断没有children
arr.push(obj(item.path, item.lable, item.icon))
}
})
return arr;
}
return (
<Sider trigger={null} >
<div style={{ display: 'flex', height: '100%', flexDirection: 'column' }}>
<div className={style.logo}>全球新闻发布管理系统</div>
<div style={{ flex: 1, overflow: 'auto' }}>
<Menu
theme="dark"
mode="inline"
selectedKeys={selectedKeys}
defaultOpenKeys={openKeys}
items={handleMenu(JSON.parse(localStorage.getItem('menu')) )}
onSelect={(item) => {
navigate(item.key)
}}
/>
</div>
</div>
</Sider>
)
}
请求过来的数据
实现后的效果
以上就是我实现的react后端路由,可能代码并不完美,有不对的地方欢迎大家指正,以免误导他人。