react reouer v6 实现后端路由


前言

闲来无事,决定用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后端路由,可能代码并不完美,有不对的地方欢迎大家指正,以免误导他人。

React Router V6实现路由守卫可以通过使用`useRoutes`钩子函数和`useNavigate`钩子函数来实现。 首先,在App.js中定义需要保护的路由,如下所示: ```jsx import { useRoutes, Navigate } from 'react-router-dom'; import Home from './pages/Home'; import Login from './pages/Login'; import Dashboard from './pages/Dashboard'; function App() { const routes = useRoutes([ { path: '/', element: <Home />, }, { path: '/login', element: <Login />, }, { path: '/dashboard', element: <Dashboard />, canActivate: () => { // 判断用户是否已经登录 const isAuthenticated = localStorage.getItem('isAuthenticated'); return isAuthenticated ? true : <Navigate to="/login" />; }, }, ]); return routes; } ``` 在定义路由时,可以添加`canActivate`函数来实现路由守卫。`canActivate`函数会在路由被访问时自动调用,可以在该函数中判断用户是否已经登录并返回`true`或`false`,如果返回`false`则该路由不会被访问,如果返回`<Navigate to="/login" />`则会自动跳转到登录页面。 接下来,在登录页面中,当用户登录成功时,可以将用户登录状态保存到LocalStorage中: ```jsx import { useNavigate } from 'react-router-dom'; function Login() { const navigate = useNavigate(); const handleLogin = () => { // 登录成功后将用户登录状态保存到LocalStorage中 localStorage.setItem('isAuthenticated', 'true'); navigate('/dashboard'); }; return ( <div> <h1>Login</h1> <button onClick={handleLogin}>Login</button> </div> ); } ``` 当用户点击登录按钮时,可以将用户登录状态保存到LocalStorage中并跳转到Dashboard页面。 最后,在Dashboard页面中,可以使用`useEffect`钩子函数和`useNavigate`钩子函数来实现退出登录功能: ```jsx import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; function Dashboard() { const navigate = useNavigate(); useEffect(() => { const handleLogout = () => { // 清除LocalStorage中的用户登录状态并跳转到登录页面 localStorage.removeItem('isAuthenticated'); navigate('/login'); }; document.addEventListener('keydown', handleLogout); return () => { document.removeEventListener('keydown', handleLogout); }; }, [navigate]); return ( <div> <h1>Dashboard</h1> </div> ); } ``` 在Dashboard页面中,可以使用`useEffect`钩子函数来监听键盘事件,当用户按下`Esc`键时,可以清除LocalStorage中的用户登录状态并跳转到登录页面。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值