<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
最近在肝一個後臺管理專案,用的是react18 + ts 路由用的是v6,當需要實現根據許可權動態載入路由表時,遇到了不少問題。
v6相比於v5做了一系列改動,通過路由表進行對映就是一個很好的改變(個人認為),但是怎麼實現根據許可權動態載入路由表呢?我也是網站上找了許多資料發現大部分還是以前版本的動態路由,要是按照現在的路由表來寫肯定是不行的。難不成又要寫成老版本那樣錯綜複雜?只能自己來手寫一個了,如有更好的方法望大佬們不吝賜教。
大致思路就是:先只在路由表設定預設路由,例如登入頁面,404頁面。再等待使用者登入成功後,獲取到使用者許可權列表和導航列表,寫一個工具函數遞迴呼叫得出路由表,在根據關鍵字對映成元件,最後返回得到新的路由表。
流程如下
紙上談來終覺淺,實際來看看吧。
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;
注意帶 //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"); } }); }; };
說一說我這裡為什麼要對映element 成對應元件這部操作,原因是我使用了redux-persist(redux持久化), 不熟悉這個外掛的可以看看我這篇文章:redux-persist若是直接轉換後存入本地再取出來渲染是會有問題的,所以需要先將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; }); };
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;
以上就是react-router v6實現動態路由範例的詳細內容,更多關於react-router v6動態路由的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45