react-router?v6實(shí)現(xiàn)動態(tài)路由實(shí)例
前言
最近在肝一個后臺管理項(xiàng)目,用的是react18 + ts 路由用的是v6,當(dāng)需要實(shí)現(xiàn)根據(jù)權(quán)限動態(tài)加載路由表時,遇到了不少問題。
v6相比于v5做了一系列改動,通過路由表進(jìn)行映射就是一個很好的改變(個人認(rèn)為),但是怎么實(shí)現(xiàn)根據(jù)權(quán)限動態(tài)加載路由表呢?我也是網(wǎng)站上找了許多資料發(fā)現(xiàn)大部分還是以前版本的動態(tài)路由,要是按照現(xiàn)在的路由表來寫肯定是不行的。難不成又要寫成老版本那樣錯綜復(fù)雜?只能自己來手寫一個了,如有更好的方法望大佬們不吝賜教。
思路
大致思路就是:先只在路由表配置默認(rèn)路由,例如登錄頁面,404頁面。再等待用戶登錄成功后,獲取到用戶權(quán)限列表和導(dǎo)航列表,寫一個工具函數(shù)遞歸調(diào)用得出路由表,在根據(jù)關(guān)鍵字映射成組件,最后返回得到新的路由表。
流程如下
- 用戶登錄成功
- 獲取用戶權(quán)限列表
- 獲取用戶導(dǎo)航菜單列表
- 根據(jù)權(quán)限和導(dǎo)航生成路由表
紙上談來終覺淺,實(shí)際來看看吧。
實(shí)現(xiàn)動態(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)識每次導(dǎo)航列表更新時,再觸發(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("用戶名或密碼錯誤"); } else { localStorage.setItem("token", res[0].username); dispatch({ type: UPDATUSERS, users: res[0] }); dispatch(getSideMenusAction()); navigate("/home"); } }); }; };
utils 工具函數(shù)處理
說一說我這里為什么要映射element 成對應(yīng)組件這部操作,原因是我使用了redux-persist(redux持久化), 不熟悉這個插件的可以看看我這篇文章:redux-persist若是直接轉(zhuǎn)換后存入本地再取出來渲染是會有問題的,所以需要先將element保存成映射路徑,然后渲染前再進(jìn)行一次路徑映射出對應(yīng)組件。
每個后臺的數(shù)據(jù)返回格式都不一樣,需要自己去轉(zhuǎn)換,我這里的轉(zhuǎn)換僅供參考。ps:defaulyRoutes和默認(rèn)router/index.ts導(dǎo)出是一樣的,可以做個小優(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 成對應(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 映射對應(yīng)組件 // handelEnd 將路由表嵌入默認(rèn)路由表得到完整路由表 const end = handelEnd(handelFilterElement(deepCopy(routs))); setrout(end); }, [routs]); return <div className="height-all">{element}</div>; } export default App;
以上就是react-router v6實(shí)現(xiàn)動態(tài)路由實(shí)例的詳細(xì)內(nèi)容,更多關(guān)于react-router v6動態(tài)路由的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
React觸發(fā)render的實(shí)現(xiàn)方法
這篇文章主要介紹了React觸發(fā)render的實(shí)現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-10-10React中獲取數(shù)據(jù)的3種方法及優(yōu)缺點(diǎn)
這篇文章主要介紹了React中獲取數(shù)據(jù)的3種方法及優(yōu)缺點(diǎn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02react 不用插件實(shí)現(xiàn)數(shù)字滾動的效果示例
這篇文章主要介紹了react 不用插件實(shí)現(xiàn)數(shù)字滾動的效果示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04