Project actual combat dynamic routing authentication

reduxjs/toolkit+ dynamic routing implementation

first step

Package redux/toolkit

 
 
1.安装
cnpm i --save-dev @reduxjs/toolkit redux
 
 
2.创建store文件
//创建唯一store对象
import { configureStore } from "@reduxjs/toolkit";

const store = configureStore({
//多个reducer合并 module模块化
reducer: {},
devTools: true,
});

export default store;
 
 
3.创建user slice切片
//用户模块下的slice
import { createSlice } from "@reduxjs/toolkit";

//创建切片
const userSlice = createSlice({
name: "User", //切片名称
//当前切片状态值
initialState: {
token: null,
routes: [],
},
reducers: {
//事件类型
saveToken(state, action) {},
saveRoutes(state, action) {},
},
});

//导出映射的action
export const { saveRoutes, saveToken } = userSlice.actions;

//导出合并的reducer
export const userReducer = userSlice.reducer;

second step

Associate store with react

Install the react-redux association

 
 
  1. cnpm i --save-dev react-redux
 
 
main.js
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";

//引入store
import store from "./store/index";
//引入react-redux
import { Provider } from "react-redux";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);

third step

 
 
在系统主界面index 下 发送网络请求获取menu菜单 解析antd menu显示。 之后做状态管理存储

//存储用户菜单到状态机
dispatch(saveRoutes(res.data.data));

对应slice中代码
reducers: {
//事件类型
saveToken(state, action) {},
saveRoutes(state, action) {
//获取参数
let { payload } = action;
state.routes = payload;
},
},

The second way of writing: perform asynchronous programming in toolkit and store directly (toolkit has a built-in asynchronous programming thunk)

toolkit built-in API to create asynchronous programming

 
 
1.在slice切片中创建异步编程
//用户模块下的slice
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
//创建异步编程
const getMenuThunk = createAsyncThunk("menuThunk", async () => {
//异步网络
return 1;
});

2.createAsyncThunk 本身和slice切片没关系,需要关联监听异步编程。
dispatch 一组 type 为 pending/fulfilled/rejected 的 action。
 
 
异步编程slice
//用户模块下的slice
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
//创建异步编程
export const getMenuThunk = createAsyncThunk("menuThunk", async (args) => {
console.log(args);
//异步网络
return 1;
});
//创建切片
const userSlice = createSlice({
name: "User", //切片名称
//当前切片状态值
initialState: {
token: null,
routes: [],
},
reducers: {
//事件类型
saveToken(state, action) {},
saveRoutes(state, action) {
//获取参数
let { payload } = action;
state.routes = payload;
},
},
//extraReducers 监听异步编程
extraReducers: {
[getMenuThunk.pending](state, action) {
console.log("pending");
},
[getMenuThunk.fulfilled](state, action) {
console.log("fulfilled", action);
},
[getMenuThunk.rejected](state, action) {
console.log("rejected");
},
},
});

//异步触发
dispatch(getMenuThunk(123));

fourth step

Start configuring routes.

Analysis: The routing menu has been state-managed. Updating the route requires updating the app.jsx component (routes configuration update --> routing update)

 
 
App.jsx
//引入路由相关
import { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { BrowserRouter } from "react-router-dom";
//路由插件
import RouterView from "react-router-waiter";
import { routes, onBeforeRoute } from "./router/index";

export default () => {
let [rots, setRots] = useState(routes);
//可以自动监听到路由数据变化
let rts = useSelector((state) => state.users.routes);
useEffect(() => {
console.log(rts);
//如果rts是[] 不需要设置
}, [rts]);
return (
<>
<BrowserRouter>
<RouterView routes={rots} onRouteBefore={onBeforeRoute}></RouterView>
</BrowserRouter>
</>
);
};

fifth step

 
 
app.jsx
根据条件实现路由数据解析
useEffect(() => {
console.log(rts);
//如果rts是[] 不需要设置
rts.length && patterRouter(rts);
}, [rts]);

//路由文件中定义数据解析方法

//路由数据解析 可以直接解析为路由的配置 解析为组件懒加载
//[{path:'',component:'',meta:{}}]
/*
component: "pms/housepay"
hidden: false
meta: {title: '商铺综合收费', icon: 'money', noCache: false, link: null}
name: "Housepay"
path: "housepay"
*/
export const patterRouter = (config) => {
//存储解析之后的路由数据
let rts = [];
//内部定义基本递归
function patter(args) {
args.forEach((item) => {
!item.children
? rts.push({
path: item.path,
component: item.component,
meta: item.meta,
})
: patter(item.children);
});
}
patter(config);
console.log(rts);
};

sixth step

Parse the sub-routing component component to achieve lazy loading

Lazy loading and parsing in routing configuration files

 
 
vite import.meta.glob("../views/**/*.jsx")
原生脚手架 ()=>import("")


//全局懒加载
const modules = import.meta.glob("../views/children/**/*.jsx");


//懒加载组件方法
function lazyLoad(path) {
let ps = modules[`../views/children/${path}.jsx`];
console.log(ps);
}


export const patterRouter = (config) => {
//存储解析之后的路由数据
let rts = [];
//内部定义基本递归
function patter(args) {
args.forEach((item) => {
!item.children
? rts.push({
path: item.path,
component: lazyLoad(item.component),//懒加载
meta: item.meta,
})
: patter(item.children);
});
}
patter(config);
console.log(rts);
};

After the lazy loading of the currently parsed routing data, the data is merged

 
 
//二级动态路由数据和静态二级合并
routeList[0].children = routeList[0].children.concat(rts);

Go to the App.jsx file to update the routes

 
 
useEffect(() => {
console.log(rts);
//如果rts是[] 不需要设置
rts && rts.length && setRots(patterRouter(rts));
}, [rts]);
//setRots 该方法更新

Test overall dynamic routing. Refreshing causes routing back to the default home page.

seventh step

Refresh the webpage, and the current route is displayed normally.

 
 
当前路由是 /housePay 刷新之后 /housePay

//将二级子路由静态 改为懒加载 执行守卫
//守卫中写入当前路径缓存
//缓存pathname路径
localStorage.setItem("_path", pathname);

Dynamically modify the secondary redirection to the last clicked route when refreshing

 
 
//读取上次的路由路径
let path = localStorage.getItem("_path");
//静态路由
let routeList = [
{
path: "/",
element: <Admin />,
children: [
{
path: "index",
component: () => import("../views/children/Index"),
},
{
path: "/",
redirect: path, //定向到上次访问路由
},
],
},

Refresh the route and return to the corresponding position of the menu

Official API

 
 
属性默认展开:
defaultOpenKeys={["//fee", "payment"]}
defaultSelectedKeys={["housepay"]}

Click the current subset to expand, and other subsets to collapse

 
 
openKeys={openKeys}
onOpenChange={onOpenChange}
//该属性放置 menu菜单一级key
//控制效果是当前展开其余收起
const rootSubmenuKeys = [
"//fee",
"//workflow",
"//owner",
"/config",
"/system",
];

openKeys={openKeys}
onOpenChange={onOpenChange}
使用之后 默认初始化展开失效
const [openKeys, setOpenKeys] = useState(["//fee", "payment"]); //默认展开

According to the analysis, the refreshed route can control the menu menu to be selected by default

 
 
defaultSelectedKeys={["housepay"]}
//使用useLocation 获取路由路径
let location = useLocation();
//默认选中为当前路由路径
defaultSelectedKeys={[location.pathname.replace(/\//g, "")]}

The menu menu automatically expands

 
 
只需要控制开开默认值
const [openKeys, setOpenKeys] = useState(["//fee", "payment"]);

//在官方提供的选中方法中存储当前用户选择
const onOpenChange = (keys) => {
//存储当前用户选择的菜单
localStorage.setItem("_hismenu", keys);

const latestOpenKey = keys.find((key) => openKeys.indexOf(key) === -1);
if (rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
setOpenKeys(keys);
} else {
setOpenKeys(latestOpenKey ? [latestOpenKey] : []);
}
};
 
 
let his = localStorage.getItem("_hismenu");
const [openKeys, setOpenKeys] = useState(his ? his.split(",") : []);
//可以实现刷新默认展开

Guess you like

Origin blog.csdn.net/m0_74331185/article/details/129996748