react-router-dom v6+ts实现路由守卫

9,819 阅读3分钟

v6的出现让我们彻底抛弃了react-router-config

1.useRoutes和Outlet

其实这两个东西配合起来就是vue的router-view了,简直一模一样,所以对vue的使用者十分友好

这两个都是v6提出来的新东西,让v6更加简便方便,useRoutes接收一个形如下图的一个路由配置数组,并返回一个路由组件,你可以将此组件放在app组件上或者任意一个地方

image.png

接下来是使用Outlet,他就是一个组件,也是一个占位符,和router-view相同,可以放在组件内部的任意一个地方

2.实现路由守卫

将上面的操作完成之后就已经实现路由的基础配置了,但是却不能实现路由守卫,仔细想一下路由守卫的作用其实就是在路由组件渲染前完成一些操作,前面也提到useRoutes(routes)返回的是一个路由组件,所以你可以自己定义一个组件,再做了一些路由守卫该做的事情后再返回useRoutes(routes)

举个栗子 在需要路由权限的路由配置中配置上auth:true,然后在每次路由跳转时根据跳转的路径递归查询到对应的路由配置对象,如果有auth:true,再进行权限判断,上代码

补充一个ts的路由配置项接口,下面要用到

export interface RouteObject {
  caseSensitive?: boolean;
  children?: RouteObject[];
  element?: React.ReactNode;
  index?: boolean;
  path?: string;
  auth?: boolean;
}
  1. 先定义查询路由的方法,此方法接收两个参数,查询的路径和完整地路由配置,并返回查询到的路由
//递归查询对应的路由
function searchroutedetail(
  path: string,
  routes: RouteObject[]
): RouteObject | null {
  for (let item of routes) {
    if (item.path === path) return item;
    if (item.children) {
      return searchroutedetail(path, item.children);
    }
  }
  return null;
}
  1. 定义路由守卫函数
//全局路由守卫
function guard(
  location: Location,//类型在react-router-dom中导入
  navigate: NavigateFunction,//类型在react-router-dom中导入
  routes: RouteObject[]
) {
  const { pathname } = location;

  //找到对应的路由信息,判断有没有权限控制
  const routedetail = searchroutedetail(pathname, routes);
  //没有找到路由,跳转404
  if (!routedetail) {
    return navigate("/404");
    // return false;
  }
  //如果需要权限验证
  if (routedetail.auth) {
    const token = localStorage.getItem("blogtoken");
    if (!token) {
      return navigate(-1);
    }
  }
}
  1. 自定义组件,实现路由守卫
export const RouterGurad = (routes: RouteObject[]) => {
  const location = useLocation();
  const navigate = useNavigate();
  let [bo, setBo] = useState(false);
  // let bo = guard(location, navigate, routes);
  useEffect(() => {
    setBo(guard(location, navigate, routes));
  }, [location, navigate, routes]);
  const Route = useRoutes(routes);

  return bo ? Route : null;
};

3.使用自定义路由守卫

import routes from "@/router";
import { RouterGurad } from "@/router/routergurad";
function App() {
  return <RouterGurad routes={routes}></RouterGurad>;
}

路由配置项

import { lazy } from "react";
// import { RouteObject } from "react-router-dom";
const T = lazy(() => import("@/pages/t"));
const HomePage = lazy(() => import("@/pages/Homepage"));

export interface RouteObject {
  caseSensitive?: boolean;
  children?: RouteObject[];
  element?: React.ReactNode;
  index?: boolean;
  path?: string;
  auth?: boolean;
}

const router: RouteObject[] = [
  {
    path: "/a",
    element: <HomePage />,
    children: [
      {
        path: "/a/t",
        element: <T />,
      },
    ],
  },
  {
    path: "/t",
    element: <HomePage />,
  },
];

export default router;

当然,实现路由守卫的方法十分灵活,例如你可以事先定义一个需要权限的路由数组,每次跳转之前先在这个数组中查询,就不要递归查询路由配置项了

最后附上源代码

import { message } from "antd";
import { useEffect } from "react";
import {
  useLocation,
  useRoutes,
  Location,
  useNavigate,
  NavigateFunction,
} from "react-router-dom";
// import { message } from "antd";

interface RouteObject {
  caseSensitive?: boolean;
  children?: RouteObject[];
  element?: React.ReactNode;
  index?: boolean;
  path?: string;
  auth?: boolean;
}

//递归查询对应的路由
export function searchroutedetail(
  path: string,
  routes: RouteObject[]
): RouteObject | null {
  for (let item of routes) {
    if (item.path === path) return item;
    if (item.children) {
      return searchroutedetail(path, item.children);
    }
  }
  return null;
}

//全局路由守卫
function guard(
  location: Location,
  navigate: NavigateFunction,
  routes: RouteObject[]
) {
  const { pathname } = location;

  //找到对应的路由信息
  const routedetail = searchroutedetail(pathname, routes);

  //没有找到路由,跳转404
  if (!routedetail) {
    // navigate("/404");
    return false;
  }

  //如果需要权限验证
  if (routedetail.auth) {
    const token = localStorage.getItem("jiang_blog_token");
    if (!token) {
      message.warn("请登录");
      navigate(-1);
      return false;
    }
  }
  return true;
}

export const RouterGurad = (routes: RouteObject[]) => {
  const location = useLocation();
  const navigate = useNavigate();
  useEffect(() => {
    guard(location, navigate, routes);
  }, [location, navigate, routes]);
  // document.documentElement.scrollTo(0, 0);
  const Route = useRoutes(routes);
  return Route;
};

使用时:

//routes是路由配置项
<Suspense>{RouterGurad(routes)}</Suspense>