網(wǎng)站首頁 編程語言 正文
前言
最近在肝一個(gè)后臺(tái)管理項(xiàng)目,用的是react18 + ts 路由用的是v6,當(dāng)需要實(shí)現(xiàn)根據(jù)權(quán)限動(dòng)態(tài)加載路由表時(shí),遇到了不少問題。
v6相比于v5做了一系列改動(dòng),通過路由表進(jìn)行映射就是一個(gè)很好的改變(個(gè)人認(rèn)為),但是怎么實(shí)現(xiàn)根據(jù)權(quán)限動(dòng)態(tài)加載路由表呢?我也是網(wǎng)站上找了許多資料發(fā)現(xiàn)大部分還是以前版本的動(dòng)態(tài)路由,要是按照現(xiàn)在的路由表來寫肯定是不行的。難不成又要寫成老版本那樣錯(cuò)綜復(fù)雜?只能自己來手寫一個(gè)了,如有更好的方法望大佬們不吝賜教。
思路
大致思路就是:先只在路由表配置默認(rèn)路由,例如登錄頁面,404頁面。再等待用戶登錄成功后,獲取到用戶權(quán)限列表和導(dǎo)航列表,寫一個(gè)工具函數(shù)遞歸調(diào)用得出路由表,在根據(jù)關(guān)鍵字映射成組件,最后返回得到新的路由表。
流程如下
- 用戶登錄成功
- 獲取用戶權(quán)限列表
- 獲取用戶導(dǎo)航菜單列表
- 根據(jù)權(quán)限和導(dǎo)航生成路由表
紙上談來終覺淺,實(shí)際來看看吧。
實(shí)現(xiàn)動(dòng)態(tài)路由
router/index.ts 默認(rèn)路由
import { lazy } from "react";
import { Navigate } from "react-router-dom";
// React 組件懶加載
// 快速導(dǎo)入工具函數(shù)
const lazyLoad = (moduleName: string) => {
const Module = lazy(() => import(`views/${moduleName}`));
return <Module />;
};
// 路由鑒權(quán)組件
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;
redux login/action.ts
注意帶 //import! 的標(biāo)識(shí)每次導(dǎo)航列表更新時(shí),再觸發(fā)路由更新action
handelFilterRouter 就是根據(jù)導(dǎo)航菜單列表 和權(quán)限列表 得出路由表的
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";
// 獲取導(dǎo)航菜單列表
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 });
// 更新導(dǎo)航菜單
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("用戶名或密碼錯(cuò)誤");
} else {
localStorage.setItem("token", res[0].username);
dispatch({ type: UPDATUSERS, users: res[0] });
dispatch(getSideMenusAction());
navigate("/home");
}
});
};
};
utils 工具函數(shù)處理
說一說我這里為什么要映射element 成對(duì)應(yīng)組件這部操作,原因是我使用了redux-persist(redux持久化), 不熟悉這個(gè)插件的可以看看我這篇文章:redux-persist若是直接轉(zhuǎn)換后存入本地再取出來渲染是會(huì)有問題的,所以需要先將element保存成映射路徑,然后渲染前再進(jìn)行一次路徑映射出對(duì)應(yīng)組件。
每個(gè)后臺(tái)的數(shù)據(jù)返回格式都不一樣,需要自己去轉(zhuǎn)換,我這里的轉(zhuǎn)換僅供參考。ps:defaulyRoutes和默認(rèn)router/index.ts導(dǎo)出是一樣的,可以做個(gè)小優(yōu)化,復(fù)用起來。
import { lazy } from "react";
import { Navigate } from "react-router-dom";
// 快速導(dǎo)入工具函數(shù)
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"),
},
];
// 權(quán)限列表 和 導(dǎo)航菜單 得出路由表 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 成對(duì)應(yīng)組件
export const handelFilterElement = (routes: any) => {
return routes.map((route: any) => {
route.element = lazyLoad(route.element);
return route;
});
};
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);
// 監(jiān)聽路由表改變重新渲染
useEffect(() => {
// deepCopy 深拷貝state數(shù)據(jù) 不能影響到store里的數(shù)據(jù)!
// handelFilterElement 映射對(duì)應(yīng)組件
// handelEnd 將路由表嵌入默認(rèn)路由表得到完整路由表
const end = handelEnd(handelFilterElement(deepCopy(routs)));
setrout(end);
}, [routs]);
return <div className="height-all">{element}</div>;
}
export default App;
原文鏈接:https://juejin.cn/post/7132393527501127687
相關(guān)推薦
- 2022-10-29 【npm 報(bào)錯(cuò) gyp info it worked if it ends with ok 大概率是
- 2022-07-04 C#使用文件流FileStream和內(nèi)存流MemoryStream操作底層字節(jié)數(shù)組byte[]_C#
- 2022-07-12 Linux虛擬機(jī)設(shè)置雙網(wǎng)卡
- 2022-04-18 react中在元素中插入多個(gè)類名, 多個(gè)狀態(tài)之間的類名切換等
- 2022-09-26 Ubuntu 22.04 源碼安裝 openssl 1.1 error while loading
- 2022-07-09 Android同步異步任務(wù)與多線程和Handler消息處理機(jī)制
- 2022-05-23 iOS實(shí)現(xiàn)簡(jiǎn)單分欄效果_IOS
- 2022-02-27 Error in render: “TypeError: Cannot read propertie
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支