路由分成两部分:
动态路由应该是根据用户登录后,根据角色的不同,从后端服务获取,因为这些数据是变化的,所以用 mobx 来管理
在src\store\路径下新建RoutesStore.tsx
- import axios from "axios";
- import {
- LoginReq,
- LoginResp,
- Menu,
- MenuAndRoute,
- Route,
- } from "../model/Student";
- import R from "../model/R";
- import { makeAutoObservable, runInAction } from "mobx";
- import { Link, Navigate, RouteObject } from "react-router-dom";
- import { load } from "../router/MyRouter";
- import A8Main from "../pages/A8Main";
- import A8NotFound from "../pages/A8NotFound";
- import { ItemType } from "antd/es/menu/hooks/useItems";
- import Icon from "./Icon";
- //其中 convertMenu 为核心方法,负责将服务器返回的 Menu 转换成 antd Menu 组件需要的 Menu
- function convertMenu(m: Menu): ItemType {
- const Label = m.routePath ? <Link to={m.routePath}>{m.label}Link> : m.label;
- return {
- key: m.key,
- label: Label,
- icon: <Icon name={m.icon}>Icon>,
- children: m.children && m.children.map(convertMenu),
- };
- }
- class RoutesStore {
- dynamicRoutes: Route[] = [];
- dynamicMenus: Menu[] = [];
- token: string = "";
- message: string = "";
- state: string = "pending";
- async login(loginReq: LoginReq) {
- this.state = "pending";
- const resp1 = await axios.post
LoginResp>>( - "http://localhost:8080/api/loginJwt",
- loginReq
- );
- if (resp1.data.code === 999) {
- const resp2 = await axios.get
MenuAndRoute>>( - `http://localhost:8080/api/menu/${loginReq.username}`
- );
- runInAction(() => {
- this.dynamicRoutes = resp2.data.data.routeList;
-
- localStorage.setItem(
- "dynamicRoutes",
- JSON.stringify(this.dynamicRoutes)
- );
- this.dynamicMenus = resp2.data.data.menuTree;
- localStorage.setItem("dynamicMenus", JSON.stringify(this.dynamicMenus));
- this.token = resp1.data.data.token;
- localStorage.setItem("token", this.token);
- this.state = "success";
- });
- } else {
- runInAction(() => {
- this.state = "error";
- this.message = resp1.data.message || "未知错误";
- });
- }
- }
- /* async fetch(username: string) {
- const resp = await axios.get
>( - `http://localhost:8080/api/menu/${username}`
- );
- runInAction(() => {
- this.dynamicRoutes = resp.data.data.routeList;
- //当在浏览器地址栏重新输入路径的时候,会重新向7070服务器发送一个请求,导致RoutesStore.tsx重新执行,
- //导致路由对象重新被创建,那么登录之后获得的动态路由数据就会丢失,所以为了防止这种情况,把登录后获得的
- //路由数据存入到localStorage中
- localStorage.setItem("dynamicRoutes", JSON.stringify(this.dynamicRoutes));
- this.dynamicMenus = resp.data.data.menuTree;
- localStorage.setItem("dynamicMenus", JSON.stringify(this.dynamicMenus));
- });
- } */
- get routes() {
- const staticRoutes: RouteObject[] = [
- {
- path: "/login",
- element: load("A8Login"),
- },
- {
- path: "/",
- element: <A8Main>A8Main>,
- children: [],
- },
- {
- path: "/404",
- element: <A8NotFound>A8NotFound>,
- },
- // 使用这个路径,上面的路径匹配不到时,显示notFound页面,但是路径还是输入的路径不变
- { path: "/*", element: <A8NotFound>A8NotFound> },
- // 使用这种路径写法的时候,上面的路径匹配不到时,页面是重定向到notFound,路径会跳转到404
- {
- path: "/*",
- element: <Navigate to={"/404"}>Navigate>,
- },
- ];
- staticRoutes[1].children = this.dynamicRoutes.map((r) => {
- return { path: r.path, element: load(r.element) };
- });
- return staticRoutes;
- }
- get menus() {
- return this.dynamicMenus.map(convertMenu);
- }
- get username() {
- if (this.token.length === 0) {
- return "";
- }
- //token 的前两部分都可以解码出来,其中 [1] 就是 token 的内容部分
- const json = atob(this.token.split(".")[1]);
- //parse方法把字符串还原成对象
- return JSON.parse(json).sub;
- }
- constructor() {
- makeAutoObservable(this);
- //页面刷新会重新调用构造器,这个时候从localStorage中获取存储的路由数据
- const routesJson = localStorage.getItem("dynamicRoutes");
- this.dynamicRoutes = routesJson ? JSON.parse(routesJson) : [];
- const menusJson = localStorage.getItem("dynamicMenus");
- this.dynamicMenus = menusJson ? JSON.parse(menusJson) : [];
- }
- reset() {
- localStorage.removeItem("dynamicRoutes");
- this.dynamicRoutes = [];
- localStorage.removeItem("dynamicMenus");
- this.dynamicMenus = [];
- localStorage.removeItem("token");
- this.token = "";
- this.state = "pending";
- }
- }
- export default new RoutesStore();
其中用 localStorage 进行了数据的持久化,避免刷新后丢失数据
跳转若发生错误,可能是因为组件懒加载引起的,需要用 Suspense 解决
- root.render(
- <ConfigProvider locale={zhCN}>
- <BrowserRouter>
- <Suspense fallback={<h3>加载中...h3>}>
- <MyRouter>MyRouter>
- Suspense>
- BrowserRouter>
- ConfigProvider>
- )