react-router?v6實(shí)現(xiàn)權(quán)限管理+自動(dòng)替換頁面標(biāo)題的案例
學(xué)了一段時(shí)間的react+ts,想著寫一個(gè)項(xiàng)目練練手,由于初次寫react+ts項(xiàng)目,有很多東西并不知道應(yīng)該怎么寫,再加上之前寫vue項(xiàng)目的習(xí)慣,突然轉(zhuǎn)換react有點(diǎn)不習(xí)慣,有很多在vue中的寫法,并不知道是否在react中仍然可行。寫項(xiàng)目之前先考慮了權(quán)限管理,第一次使用react-router v6 也不知道是否有更好的寫法。這次就來簡(jiǎn)單分享一下我實(shí)現(xiàn)權(quán)限管理以及攔截器中遇到的一些問題。
權(quán)限管理
這次項(xiàng)目是有三種權(quán)限,分別是用戶,商家以及管理員。這次寫的權(quán)限管理是高級(jí)權(quán)限能訪問低級(jí)權(quán)限的所有頁面,但是低級(jí)權(quán)限不能訪問高級(jí)權(quán)限的頁面。
簡(jiǎn)單實(shí)現(xiàn)
// 在這里進(jìn)行判斷用戶權(quán)限使用不同的頁面
import React from "react";
import Merchant from "../pages/merchant";
import Admin from "../pages/admin";
import User from "../pages/user";
export default function Index() {
//這里使用power來判斷是否登錄,以及權(quán)限
let power = localStorage.getItem("power");
return (
<div>
{power ? (
power === "1" ? (
<Merchant></Merchant>
) : (
<Admin></Admin>
)
) : (
<User></User>
)}
</div>
);
}我實(shí)現(xiàn)權(quán)限管理的方式就是根據(jù)不同的權(quán)限掛載不同的頁面,在頁面中實(shí)現(xiàn)掛載不同的路由。
之后看一下我在三種不同頁面中的路由定義以及具體內(nèi)容,即不需要顯示的路由就不應(yīng)該被注冊(cè)
user路由表
// 引入路由
import Login from "../pages/user/login";
import Register from "../pages/user/register";
import NoPower from "../pages/error/noPower";
import NoPage from "../pages/error/noPage";
import { myRouteObj } from "../type";
//這里的myRouteObj是我自定義的一個(gè)類型用于拓展title實(shí)現(xiàn)頁面標(biāo)題的自動(dòng)切換
export const userRoutes: myRouteObj[] = [
{
path: "/404",
element: <NoPage></NoPage>,
title: '404'
},
{
path: "/login",
element: <Login></Login>,
title: '登錄'
},
{
path: "/register",
element: <Register></Register>,
title: '注冊(cè)'
},
];
const routes = [
...userRoutes,
{
path: "*",
element: <NoPower></NoPower>,
title: '沒有權(quán)限'
},
];
export default routes;路由表中暴露了兩個(gè)對(duì)象,分別是最"原始的對(duì)象"和使用的對(duì)象,這兩個(gè)對(duì)象的區(qū)別就是是否有攔截器,為什么需要把這個(gè)給區(qū)分開,是因?yàn)槲覀兊哪康氖歉邫?quán)限的用戶能訪問到低權(quán)限用戶所能訪問的頁面,又由于我們是通過返回不同的頁面來實(shí)現(xiàn)的權(quán)限管理(即使用過程中必須高權(quán)限的路由中包含低權(quán)限的路由)而且攔截器必須要放在最后,所以就分了兩個(gè)來暴露,一個(gè)是供注冊(cè)路由使用(有攔截器版本),另一個(gè)是由高級(jí)權(quán)限路由表合并使用
myRouteObj的內(nèi)容
import { NonIndexRouteObject } from "react-router";
export interface myRouteObj extends NonIndexRouteObject {
title?: string;
}user頁面的入口文件
import React from "react";
import userRoutes from "../../router/user";
import { useMyRoutes } from "../../hooks/route";
export default function User() {
//這里的useMyRoutes是我自己封裝的一個(gè)鉤子函數(shù)
const element = useMyRoutes(userRoutes);
return <div>{element}</div>;
}useMyRoutes鉤子函數(shù)的具體內(nèi)容
該鉤子函數(shù)用于更換頁面標(biāo)題以及一級(jí)路由表中是否有該路由(沒有的話會(huì)跳到404),這里為什么只判斷了一級(jí)路由表是因?yàn)槲覀€(gè)人感覺一級(jí)路由如果輸入錯(cuò)誤,其就應(yīng)該跳轉(zhuǎn)到404頁面,而二級(jí)路由輸入錯(cuò)誤,應(yīng)該重定向到二級(jí)路由正確的位置,這樣我感覺對(duì)用戶要更友好一點(diǎn),所以我在二級(jí)路由中添加了重定向的功能,并且也實(shí)現(xiàn)了替換標(biāo)題的功能,如果沒有標(biāo)題的話會(huì)返回一個(gè)默認(rèn)值
import { useEffect } from "react";
import { useLocation, useNavigate, useRoutes } from "react-router";
import { myRouteObj } from "../type";
function flatDeep(children: any, findArr: any): string {
// 判斷children數(shù)組中是否有想要的元素
let tempArr = children.map((item: any) => item.path);
let index = tempArr.indexOf(findArr[0]);
if (index !== -1) {
return findArr.length > 1
? flatDeep(children[index], findArr.splice(1, findArr.length))
: children[index].title;
? children[index].title
: "商城";
}
return "商城";
}
export function useMyRoutes(routes: myRouteObj[]) {
// 判斷路由表中是否有對(duì)應(yīng)路由,如果沒有就返回到404
const location = useLocation();
const navigate = useNavigate();
useEffect(() => {
/*
這里有一個(gè)小問題,困擾了我半天,就是原本我獲取路徑是寫在useEffect外邊的,頁面每次刷新就會(huì)顯示成錯(cuò)誤的標(biāo)題,最后輸出是因?yàn)閳?bào)存的值沒有更新,所以就改到useEffect函數(shù)里邊了
*/
// 非空
const firstPathName = location.pathname
.split("/")
.filter((item) => item !== "");
const firstPath = routes.map((item) => item.path?.split("/")[1]);
let index = firstPath.indexOf(firstPathName[0]);
if (!index) {
navigate("/404");
return;
}
// 判斷是否是首頁
if (location.pathname === "/") {
document.title = "商城";
return;
}
// 判斷是幾級(jí)路由
if (firstPathName.length > 1) {
// 多級(jí)
document.title = flatDeep(
routes[index].children,
firstPathName.splice(1, firstPathName.length)
);
return;
}
// 一級(jí)
document.title = routes[index].title
? (routes[index].title as string)
: "商城";
}, [location, navigate, routes]);
return useRoutes(routes);
}商家的路由表
import { Navigate } from "react-router";
import Merchant from "../pages/merchant";
import Commodity from "../pages/merchant/commodity";
import { userRoutes } from "./user";
import { getRoutesObj } from "../utils";
import { myRouteObj } from "../type";
const children: myRouteObj[] = [
{
path: "commodity",
element: <Commodity></Commodity>,
},
];
const primitiveObj = {
path: "/merchant/*",
element: <Merchant></Merchant>,
title: '商店后臺(tái)'
};
// 有攔截器版本
export const [routes, defaultRoutes] = getRoutesObj(
userRoutes,
primitiveObj,
children,
{
path: "*",
element: <Navigate to="/merchant/commodity"></Navigate>,
}
);getRoutesObj是我自定義的為二級(jí)路由添加重定向的工具函數(shù),至于為什么封裝這個(gè)函數(shù),其原因就是商家和管理員都要使用,原本是想把重定向添加到最外層,但是這個(gè)由于react-router v6并不是嚴(yán)格匹配模式,所以即使匹配上路由也會(huì)嘗試往下匹配更精確的路由比如/about和/about:id所以封裝了該函數(shù)添加二級(jí)攔截器
import { myRouteObj } from "../type";
// 第一個(gè)參數(shù)是復(fù)用的數(shù)組對(duì)象,第二個(gè)參數(shù)是有攔截器的對(duì)象
/**
*生成路由表配置(加攔截器)
* @export
* @param {myRouteObj[]} reuseObj 復(fù)用的數(shù)組對(duì)象
* @param {myRouteObj} targetObj 目標(biāo)對(duì)象
* @param {myRouteObj[]} childrenObj 子級(jí)路由
* @param {myRouteObj} globalinterceptorObj 全局?jǐn)r截路由
* @param {myRouteObj} interceptor 可選攔截器對(duì)象(添加子級(jí)路由中)
*/
export function getRoutesObj(
reuseObj: myRouteObj[],
targetObj: myRouteObj,
childrenObj: myRouteObj[],
interceptorObj?: myRouteObj
) {
// 先復(fù)制一份children
const children = [...childrenObj];
// 判斷是否有值
if (interceptorObj) {
children.push(interceptorObj);
}
return [
[
...reuseObj,
{
...targetObj,
children: children,
},
],
[
...reuseObj,
{
...targetObj,
children: [...childrenObj],
},
],
];
}商家入口文件
import React, { Fragment } from "react";
import { Outlet } from "react-router";
import { routes } from "../../router/merchant";
import { useMyRoutes } from "../../hooks/route";
import { myRouteObj } from "../../type";
export default function Merchant() {
const element = useMyRoutes(routes as myRouteObj[]);
return (
<Fragment>
{element}
<Outlet></Outlet>
</Fragment>
);
}管理員的和商家的差不多,這里就不再過多敘述了
總結(jié)
實(shí)現(xiàn)這個(gè)權(quán)限管理還是花了有一天多的時(shí)間,有很多東西沒有這么寫過,也不知道正規(guī)的是否應(yīng)該這樣寫,不過現(xiàn)在還是先實(shí)現(xiàn)功能就行了,等之后隨著不斷的練習(xí)和學(xué)習(xí),也能檢驗(yàn)我這個(gè)方法是否是對(duì)的,第一次寫react+TS的項(xiàng)目,仍有很多不足,如果哪位大佬有覺得不妥的地方,也歡迎指出,一同學(xué)習(xí),共同進(jìn)步。
到此這篇關(guān)于react-router v6實(shí)現(xiàn)權(quán)限管理+自動(dòng)替換頁面標(biāo)題的文章就介紹到這了,更多相關(guān)react-router v6權(quán)限內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React找不到模塊“./index.module.scss”或其相應(yīng)的類型聲明及解決方法
這篇文章主要介紹了React找不到模塊“./index.module.scss”或其相應(yīng)的類型聲明及解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09
React通過父組件傳遞類名給子組件的實(shí)現(xiàn)方法
React 是一個(gè)用于構(gòu)建用戶界面的 JAVASCRIPT 庫。這篇文章主要介紹了React通過父組件傳遞類名給子組件的方法,需要的朋友可以參考下2017-11-11
react組件實(shí)例屬性props實(shí)例詳解
這篇文章主要介紹了react組件實(shí)例屬性props,本文結(jié)合實(shí)例代碼給大家簡(jiǎn)單介紹了props使用方法,代碼簡(jiǎn)單易懂,需要的朋友可以參考下2023-01-01
React Hooks常用場(chǎng)景的使用(小結(jié))
這篇文章主要介紹了React Hooks常用場(chǎng)景的使用,根據(jù)使用場(chǎng)景分別進(jìn)行舉例說明,幫助你認(rèn)識(shí)理解并可以熟練運(yùn)用 React Hooks 大部分特性,感興趣的可以了解一下2021-04-04
react數(shù)據(jù)管理機(jī)制React.Context源碼解析
這篇文章主要為大家介紹了react數(shù)據(jù)管理機(jī)制React.Context源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
使用React?MUI庫實(shí)現(xiàn)用戶列表分頁功能
MUI是一款基于React的UI組件庫,可以方便地構(gòu)建美觀的用戶界面,使用MUI的DataTable組件和分頁器組件可以輕松實(shí)現(xiàn)用戶列表分頁功能,這篇文章使用MUI庫實(shí)現(xiàn)了用戶列表分頁功能,感興趣的同學(xué)可以參考下文2023-05-05

