夜猫子的知识栈 夜猫子的知识栈
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

夜猫子

前端练习生
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 核心概念

  • 高级指引

  • Hook

  • 案例演示

  • Router

    • 详细使用示例
    • 动态路由
      • 实现动态路由
        • router/index.ts 默认路由
        • redux login/action.ts
        • utils 工具函数处理
        • App.tsx
    • 路由传参
  • Redux

  • Jotai

  • Mobx

  • 其他

  • 《React》笔记
  • Router
夜猫子
2022-11-04
目录

动态路由

# 实现动态路由

# router/index.ts 默认路由

import { lazy } from "react";
import { Navigate } from "react-router-dom";

// React 组件懒加载

// 快速导入工具函数
const lazyLoad = (moduleName: string) => {
  const Module = lazy(() => import(`views/${moduleName}`));
  return <Module />;
};
// 路由鉴权组件
const Appraisal = ({ children }: any) => {
  const token = localStorage.getItem("token");
  return token ? children : <Navigate to="/login" />;
};

interface Router {
  name?: string;
  path: string;
  children?: Array<Router>;
  element: any;
}

const routes: Array<Router> = [
  {
    path: "/login",
    element: lazyLoad("login"),
  },
  {
    path: "/",
    element: <Appraisal>{lazyLoad("sand-box")}</Appraisal>,
    children: [
      {
        path: "",
        element: <Navigate to="home" />,
      },
      {
        path: "*",
        element: lazyLoad("sand-box/nopermission"),
      },
    ],
  },
  {
    path: "*",
    element: lazyLoad("not-found"),
  },
];


export default routes;
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

# redux login/action.ts

注意带 //import! 的标识 每次导航列表更新时,再触发路由更新action handelFilterRouter 就是根据导航菜单列表 和权限列表 得出路由表的

import { INITSIDEMENUS, UPDATUSERS, LOGINOUT, UPDATROUTES } from "./contant";
import { getSideMenus } from "services/home";
import { loginUser } from "services/login";
import { patchRights } from "services/right-list";
import { handleSideMenu } from "@/utils/devUtils";
import { handelFilterRouter } from "@/utils/routersFilter";
import { message } from "antd";

// 获取导航菜单列表
export const getSideMenusAction = (): any => {
  return (dispatch: any, state: any) => {
    getSideMenus().then((res: any) => {
      const rights = state().login.users.role.rights;
      const newMenus = handleSideMenu(res, rights);
      dispatch({ type: INITSIDEMENUS, menus: newMenus });
      dispatch(updateRoutesAction()); //import!
    });
  };
};

// 退出登录
export const loginOutAction = (): any => ({ type: LOGINOUT });

// 更新导航菜单
export const updateMenusAction = (item: any): any => {
  return (dispatch: any) => {
    patchRights(item).then((res: any) => {
      dispatch(getSideMenusAction());
    });
  };
};

// 路由更新 //import!
export const updateRoutesAction = (): any => {
  return (dispatch: any, state: any) => {
    const rights = state().login.users.role.rights;
    const menus = state().login.menus;
    const routes = handelFilterRouter(rights, menus); //import!
    dispatch({ type: UPDATROUTES, routes });
  };
};

// 登录
export const loginUserAction = (item: any, navigate: any): any => {
  return (dispatch: any) => {
    loginUser(item).then((res: any) => {
      if (res.length === 0) {
        message.error("用户名或密码错误");
      } else {
        localStorage.setItem("token", res[0].username);
        dispatch({ type: UPDATUSERS, users: res[0] });
        dispatch(getSideMenusAction());
        navigate("/home");
      }
    });
  };
};

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

# utils 工具函数处理

说一说我这里为什么要映射element 成对应组件这部操作,原因是我使用了redux-persist(redux持久化。

若是直接转换后存入本地再取出来渲染是会有问题的,所以需要先将element保存成映射路径,然后渲染前再进行一次路径映射出对应组件。

每个后台的数据返回格式都不一样,需要自己去转换,我这里的转换仅供参考。 ps:defaulyRoutes和默认router/index.ts导出是一样的,可以做个小优化,复用起来。

import { lazy } from "react";
import { Navigate } from "react-router-dom";
// 快速导入工具函数
const lazyLoad = (moduleName: string) => {
  const Module = lazy(() => import(`views/${moduleName}`));
  return <Module />;
};
const Appraisal = ({ children }: any) => {
  const token = localStorage.getItem("token");
  return token ? children : <Navigate to="/login" />;
};

const defaulyRoutes: any = [
  {
    path: "/login",
    element: lazyLoad("login"),
  },
  {
    path: "/",
    element: <Appraisal>{lazyLoad("sand-box")}</Appraisal>,
    children: [
      {
        path: "",
        element: <Navigate to="home" />,
      },
      {
        path: "*",
        element: lazyLoad("sand-box/nopermission"),
      },
    ],
  },
  {
    path: "*",
    element: lazyLoad("not-found"),
  },
];

// 权限列表 和 导航菜单 得出路由表 element暂用字符串表示 后面渲染前再映射
export const handelFilterRouter = (
  rights: any,
  menus: any,
  routes: any = []
) => {
  for (const menu of menus) {
    if (menu.pagepermisson) {
      let index = rights.findIndex((item: any) => item === menu.key) + 1;
      if (!menu.children) {
        if (index) {
          const obj = {
            path: menu.key,
            element: `sand-box${menu.key}`,
          };
          routes.push(obj);
        }
      } else {
        handelFilterRouter(rights, menu.children, routes);
      }
    }
  }
  return routes;
};

// 返回最终路由表
export const handelEnd = (routes: any) => {
  defaulyRoutes[1].children = [...routes, ...defaulyRoutes[1].children];
  return defaulyRoutes;
};

// 映射element 成对应组件
export const handelFilterElement = (routes: any) => {
  return routes.map((route: any) => {
    route.element = lazyLoad(route.element);
    return route;
  });
};

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

# App.tsx

import routes from "./router";
import { useRoutes } from "react-router-dom";
import { shallowEqual, useSelector } from "react-redux";
import { useState, useEffect } from "react";
import { handelFilterElement, handelEnd } from "@/utils/routersFilter";
import { deepCopy } from "@/utils/devUtils";

function App() {
  console.log("first");
  const [rout, setrout] = useState(routes);
  const { routs } = useSelector(
    (state: any) => ({ routs: state.login.routes }),
    shallowEqual
  );

  const element = useRoutes(rout);
  // 监听路由表改变重新渲染
  useEffect(() => {
  // deepCopy 深拷贝state数据 不能影响到store里的数据!
  // handelFilterElement 映射对应组件
  // handelEnd 将路由表嵌入默认路由表得到完整路由表
    const end = handelEnd(handelFilterElement(deepCopy(routs)));
    setrout(end);
  }, [routs]);

  return <div className="height-all">{element}</div>;
}

export default App;
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
编辑 (opens new window)
上次更新: 2023/7/11 18:57:41
详细使用示例
路由传参

← 详细使用示例 路由传参→

最近更新
01
IoC 解决了什么痛点问题?
03-10
02
如何调试 Nest 项目
03-10
03
Provider注入对象
03-10
更多文章>
Copyright © 2019-2025 Study | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式