react_14

动态路由

路由分成两部分:

  • 静态路由,固定的部分,如主页、404、login 这几个页面

  • 动态路由,变化的部分,经常是主页内的嵌套路由,比如 Student、Teacher 这些

动态路由应该是根据用户登录后,根据角色的不同,从后端服务获取,因为这些数据是变化的,所以用 mobx 来管理

在src\store\路径下新建RoutesStore.tsx

TypeScript 复制代码
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<R<LoginResp>>(
      "http://localhost:8080/api/loginJwt",
      loginReq
    );
    if (resp1.data.code === 999) {
      const resp2 = await axios.get<R<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<R<MenuAndRoute>>(
      `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 解决

TypeScript 复制代码
root.render(
  <ConfigProvider locale={zhCN}>
    <BrowserRouter>
      <Suspense fallback={<h3>加载中...</h3>}>
        <MyRouter></MyRouter>
      </Suspense>
    </BrowserRouter>
  </ConfigProvider>
)
相关推荐
逐·風2 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
Devil枫2 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
尚梦3 小时前
uni-app 封装刘海状态栏(适用小程序, h5, 头条小程序)
前端·小程序·uni-app
GIS程序媛—椰子3 小时前
【Vue 全家桶】6、vue-router 路由(更新中)
前端·vue.js
前端青山4 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
毕业设计制作和分享4 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
从兄5 小时前
vue 使用docx-preview 预览替换文档内的特定变量
javascript·vue.js·ecmascript
清灵xmf6 小时前
在 Vue 中实现与优化轮询技术
前端·javascript·vue·轮询
大佩梨6 小时前
VUE+Vite之环境文件配置及使用环境变量
前端
GDAL6 小时前
npm入门教程1:npm简介
前端·npm·node.js