react-router v6实现权限管理+自动替换页面标题的案例

 更新时间:2023年05月14日 17:06:33   作者:BUG不加糖  
这篇文章主要介绍了react-router v6实现权限管理+自动替换页面标题,这次项目是有三种权限,分别是用户,商家以及管理员,这次写的权限管理是高级权限能访问低级权限的所有页面,但是低级权限不能访问高级权限的页面,需要的朋友可以参考下

学了一段时间的react+ts,想着写一个项目练练手,由于初次写react+ts项目,有很多东西并不知道应该怎么写,再加上之前写vue项目的习惯,突然转换react有点不习惯,有很多在vue中的写法,并不知道是否在react中仍然可行。写项目之前先考虑了权限管理,第一次使用react-router v6 也不知道是否有更好的写法。这次就来简单分享一下我实现权限管理以及拦截器中遇到的一些问题。

权限管理

这次项目是有三种权限,分别是用户,商家以及管理员。这次写的权限管理是高级权限能访问低级权限的所有页面,但是低级权限不能访问高级权限的页面。
简单实现

// 在这里进行判断用户权限使用不同的页面
import React from "react";
import Merchant from "../pages/merchant";
import Admin from "../pages/admin";
import User from "../pages/user";
export default function Index() {
//这里使用power来判断是否登录,以及权限
  let power = localStorage.getItem("power");
  return (
    <div>
      {power ? (
        power === "1" ? (
          <Merchant></Merchant>
        ) : (
          <Admin></Admin>
        )
      ) : (
        <User></User>
      )}
    </div>
  );
}

我实现权限管理的方式就是根据不同的权限挂载不同的页面,在页面中实现挂载不同的路由。
之后看一下我在三种不同页面中的路由定义以及具体内容,即不需要显示的路由就不应该被注册
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是我自定义的一个类型用于拓展title实现页面标题的自动切换
export const userRoutes: myRouteObj[] = [
  {
    path: "/404",
    element: <NoPage></NoPage>,
    title: '404'
  },
  {
    path: "/login",
    element: <Login></Login>,
    title: '登录'
  },
  {
    path: "/register",
    element: <Register></Register>,
    title: '注册'
  },
];
const routes = [
  ...userRoutes,
  {
    path: "*",
    element: <NoPower></NoPower>,
    title: '没有权限'
  },
];
export default routes;

路由表中暴露了两个对象,分别是最"原始的对象"和使用的对象,这两个对象的区别就是是否有拦截器,为什么需要把这个给区分开,是因为我们的目的是高权限的用户能访问到低权限用户所能访问的页面,又由于我们是通过返回不同的页面来实现的权限管理(即使用过程中必须高权限的路由中包含低权限的路由)而且拦截器必须要放在最后,所以就分了两个来暴露,一个是供注册路由使用(有拦截器版本),另一个是由高级权限路由表合并使用

myRouteObj的内容

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是我自己封装的一个钩子函数
  const element = useMyRoutes(userRoutes);
  return <div>{element}</div>;
}

useMyRoutes钩子函数的具体内容
该钩子函数用于更换页面标题以及一级路由表中是否有该路由(没有的话会跳到404),这里为什么只判断了一级路由表是因为我个人感觉一级路由如果输入错误,其就应该跳转到404页面,而二级路由输入错误,应该重定向到二级路由正确的位置,这样我感觉对用户要更友好一点,所以我在二级路由中添加了重定向的功能,并且也实现了替换标题的功能,如果没有标题的话会返回一个默认值

import { useEffect } from "react";
import { useLocation, useNavigate, useRoutes } from "react-router";
import { myRouteObj } from "../type";
function flatDeep(children: any, findArr: any): string {
  // 判断children数组中是否有想要的元素
  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[]) {
  // 判断路由表中是否有对应路由,如果没有就返回到404
  const location = useLocation();
  const navigate = useNavigate();
  useEffect(() => {
  /*
这里有一个小问题,困扰了我半天,就是原本我获取路径是写在useEffect外边的,页面每次刷新就会显示成错误的标题,最后输出是因为报存的值没有更新,所以就改到useEffect函数里边了
*/
    // 非空
    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;
    }
    // 判断是几级路由
    if (firstPathName.length > 1) {
      // 多级
      document.title = flatDeep(
        routes[index].children,
        firstPathName.splice(1, firstPathName.length)
      );
      return;
    }
    // 一级
    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: '商店后台'
};
// 有拦截器版本
export const [routes, defaultRoutes] = getRoutesObj(
  userRoutes,
  primitiveObj,
  children,
  {
    path: "*",
    element: <Navigate to="/merchant/commodity"></Navigate>,
  }
);

getRoutesObj是我自定义的为二级路由添加重定向的工具函数,至于为什么封装这个函数,其原因就是商家和管理员都要使用,原本是想把重定向添加到最外层,但是这个由于react-router v6并不是严格匹配模式,所以即使匹配上路由也会尝试往下匹配更精确的路由比如/about和/about:id所以封装了该函数添加二级拦截器

import { myRouteObj } from "../type";
// 第一个参数是复用的数组对象,第二个参数是有拦截器的对象
/**
 *生成路由表配置(加拦截器)
 * @export
 * @param {myRouteObj[]} reuseObj 复用的数组对象
 * @param {myRouteObj} targetObj 目标对象
 * @param {myRouteObj[]} childrenObj 子级路由
 * @param {myRouteObj} globalinterceptorObj 全局拦截路由
 * @param {myRouteObj} interceptor 可选拦截器对象(添加子级路由中)
 */
export function getRoutesObj(
  reuseObj: myRouteObj[],
  targetObj: myRouteObj,
  childrenObj: myRouteObj[],
  interceptorObj?: myRouteObj
) {
  //  先复制一份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>
  );
}

管理员的和商家的差不多,这里就不再过多叙述了

总结

实现这个权限管理还是花了有一天多的时间,有很多东西没有这么写过,也不知道正规的是否应该这样写,不过现在还是先实现功能就行了,等之后随着不断的练习和学习,也能检验我这个方法是否是对的,第一次写react+TS的项目,仍有很多不足,如果哪位大佬有觉得不妥的地方,也欢迎指出,一同学习,共同进步。

到此这篇关于react-router v6实现权限管理+自动替换页面标题的文章就介绍到这了,更多相关react-router v6权限内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 一文学会使用Remix写API接口

    一文学会使用Remix写API接口

    这篇文章主要为大家介绍了一文学会Remix写API接口实现示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • React找不到模块“./index.module.scss”或其相应的类型声明及解决方法

    React找不到模块“./index.module.scss”或其相应的类型声明及解决方法

    这篇文章主要介绍了React找不到模块“./index.module.scss”或其相应的类型声明及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-09-09
  • React通过父组件传递类名给子组件的实现方法

    React通过父组件传递类名给子组件的实现方法

    React 是一个用于构建用户界面的 JAVASCRIPT 库。这篇文章主要介绍了React通过父组件传递类名给子组件的方法,需要的朋友可以参考下
    2017-11-11
  • react组件实例属性props实例详解

    react组件实例属性props实例详解

    这篇文章主要介绍了react组件实例属性props,本文结合实例代码给大家简单介绍了props使用方法,代码简单易懂,需要的朋友可以参考下
    2023-01-01
  • React Hooks常用场景的使用(小结)

    React Hooks常用场景的使用(小结)

    这篇文章主要介绍了React Hooks常用场景的使用,根据使用场景分别进行举例说明,帮助你认识理解并可以熟练运用 React Hooks 大部分特性,感兴趣的可以了解一下
    2021-04-04
  • 简单分析React中的EffectList

    简单分析React中的EffectList

    这篇文章主要简单分析了React中的EffectList,帮助大家更好的理解和学习使用React进行前端开发,感兴趣的朋友可以了解下
    2021-04-04
  • React翻页器的实现(包含前后端)

    React翻页器的实现(包含前后端)

    本文主要介绍了React翻页器的实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • react数据管理机制React.Context源码解析

    react数据管理机制React.Context源码解析

    这篇文章主要为大家介绍了react数据管理机制React.Context源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • 使用React MUI库实现用户列表分页功能

    使用React MUI库实现用户列表分页功能

    MUI是一款基于React的UI组件库,可以方便地构建美观的用户界面,使用MUI的DataTable组件和分页器组件可以轻松实现用户列表分页功能,这篇文章使用MUI库实现了用户列表分页功能,感兴趣的同学可以参考下文
    2023-05-05
  • React html中使用react的两种方式

    React html中使用react的两种方式

    这篇文章主要介绍了React html中使用react的两种方式,本文给大家提到了React pwa的配置代码,给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04

最新评论