React中基于Router-V6实现配置式路由与路由鉴权详细过程

3,629 阅读2分钟

在Vue和Umi中都有集成预置的路由集中配置与鉴权,非常的方便好用;但是在默认React项目中是编程式路由,将需要手动进行配置式路由构建与权限构建;RouterV6中提供了一个与(router-view)具有相同功能的(Outlet)组件,可以直接给子路由指定出口位置,那么我们就可以实现一个类似Vue一样的路由;

  1. 具体流程:
(1)安装 npm i react-router-dom@6.3.0
(2)src目录下建一个Router文件夹,内部创建一个routerConfig.tsx文件;
(3)进行路由懒加载;
import Loadable from "react-loadable";
const LoadingTip = () => <div>加载路由ing...</div>;
const Login = Loadable({
  loader: () => import("@/pages/rootLevelPages/login/Login"),
  loading: LoadingTip,
});
(4)指定路由结构;
export type RoutesItems = {
  path: string;
  element: React.ReactElement;
  children?: RoutesItems[];
};
(5)配置路由表(注意element后面需要写成<.../>形式);
export const rootRouterConfig:RoutesItems[]  = [
  {
    path: "/",
    element: <Login />,
  },
  {
    path: "login",
    element: <Login />,
  },
  {
    path: "home",
    element: <Home />,
    children: [
      {
        path: "*",
        element: <Navigate to={"/home/userManage/userPassword"} />,
      },
      {
        path: "userManage/userPassword",
        element: <UserPassword />,
        children: [
          { path: "change", element: <p>修改密码页面</p> },
          { path: "reset", element: <p>重置密码页面</p> },
          { path: "fingerprint", element: <p>指纹密码页面</p> },
        ],
      },
      {
        path: "userManage/userInfo",
        element: <UserInfo />,
        children: [
          {
            path: "detail:id",
            element: <UserDetail />,
          },
          {
            path: "avator:id",
            element: <AvatorDetail />,
          },
        ],
      },
      {
        path: "computerManage/computerInfo",
        element: <ComputerInfo />,
      },
      {
        path: "computerManage/computerDevice",
        element: <ComputerDevice />,
      },
      {
        path: "notifyManage/notifyInfo",
        element: <NotifyInfo />,
      },
      {
        path: "notifyManage/notifyData",
        element: <NotifyData />,
      },
      { path: "*", element: <p>ERROR-PAGE</p> },
    ],
  },
  { path: "*", element: <p>ERROR-PAGE;</p> },
  { path: "error", element: <p>ERROR-PAGE-对不起-您没有该路由权限;</p> },
];
如果是直接配置路由组件是写成这种结构,是可以直接进行内部嵌套,也就是父子路由,那么就可以给子路由制定Outlet出口;
<Route path="home" element={ <Home/>}>
  <Route path="userManage/userPassword" element={<UserPassword/>} />
  <Route path="userManage/userInfo" element={<UserInfo/>} /> 
</Route>
注意path是可以不用"/"链接父子路径的,会自动处理;
最后会访问home/userManage/userPassword对应的组件;

(6)这个时候我们有了Config就只需要将它渲染成上面的这种嵌套结构即可;
   使用一个小递归进行处理:
   const renderRoutes = (routes: RoutesItems[]) => {
    return routes.map((item: RoutesItems, index: number) => {
      if (item && item.children) {
        return (
          <Route path={item.path} element={item.element} key={index}>
            {renderRoutes(item.children)}
          </Route>
        );
      } else {
        return (
          <Route path={item.path} element={item.element} key={index}></Route>
        );
      }
    });
  };
  return (
    <>
      <Routes>{renderRoutes(rootRouterConfig)}</Routes>
    </>
  );
(7)在父级组件<Home/>中指定Outlet给予子路由出口;
   <Content>
        <Outlet/>
        //子路由显示在此处;
   </Content>
现在就已经实现了配置式路由,可以很方便的操作RouterConfig就行了,其它的事情可以不用关心了;
当然也可以直接使用V6自带的useRoutes()API;这样写可以获得更多扩展性;

(8)路由鉴权,RouterV6中通过useLocation可以获取到当前路由地址,我们再写一个自定义的验证Hook比对其进行拦截;
例如通过一个请求Api获得当前用户的路由权限列表,赋值给Context;
const { state, dispatch } = useContext(ContentContext);
useEffect(() => {
    dispatch({type:"userRouterPermissions",payload:['/home/computerManage/computerInfo','/home/userManage/userPassword']});
}, []);
注意防止强刷需要在App.js中进行获取;

(9)封装一个自定义验证hook;
import { ContentContext } from "@/context/ContextProvider";
import { useContext } from "react";
export default function useRouterAuth(currentPathname:string) {
  const { state } = useContext(ContentContext);
  console.log("登陆账号路由权限列表", state.routerPermissions,"当前进入的路由地址",currentPathname);
  const AuthResult: boolean = state.routerPermissions.find(
    (x: string) => x === currentPathname
  )
    ? true
    : false;
  return {
    AuthResult,
  };
}

(10)在访问页面中进行调用;
  let navigate = useNavigate();
  let location = useLocation();
  const { AuthResult } = useRouterAuth(location.pathname);
  if (!AuthResult) {
    navigate("/error");
  }
(11)完工,愉快的配置路由即可;