react-router-config使用与路由鉴权

9,157 阅读3分钟

react-router-config

Static route config helpers

github: react-router-config

使用

需要注意的是,page2有嵌套路由,则需要在page2中再写一遍renderRoutes,并且 renderRoutes(routes)的routes是在props中获取的,即应该写成renderRoutes(props.route.routes)

import React from "react";
import { renderRoutes } from "react-router-config";
import { HashRouter, Redirect } from "react-router-dom";

const routes = [
  { path: "/", exact: true, render: () => <Redirect to={"/page1"} /> },
  { path: "/page1", component: Page1 },
  {
    path: "/page2",
    component: Page2,
    routes: [
      {
        path: "/page2/child",
        component: Child
      }
    ]
  }
];

function App() {
  return (
    <HashRouter>
      <div className="App">
        <h1>Hello</h1>
        {renderRoutes(routes)}
      </div>
    </HashRouter>
  );
}

renderRoutes源码

其实就是把routes做了个map。

根据源码,就很容易做个类似vue的路由守卫了。

import React from "react";
import { Switch, Route } from "react-router";

function renderRoutes(routes, extraProps = {}, switchProps = {}) {
  return routes ? (
    <Switch {...switchProps}>
      {routes.map((route, i) => (
        <Route
          key={route.key || i}
          path={route.path}
          exact={route.exact}
          strict={route.strict}
          render={props =>
            route.render ? (
              route.render({ ...props, ...extraProps, route: route })
            ) : (
              <route.component {...props} {...extraProps} route={route} />
            )
          }
        />
      ))}
    </Switch>
  ) : null;
}

export default renderRoutes;

路由鉴权

以下是个人重写,添加了权限验证以及多重路由匹配(即添加渲染非<Switch>包裹的<Route>)。

import React from "react";
import { Switch, Route, Redirect } from "react-router";

/**
 * 将路由配置渲染成节点
 * @param {Array} routes switch路由列表
 * @param {String|Number} authed 当前账号权限,不传则可以访问该routes列表的所有路由
 * @param {Array} multipleRoutes 非switch路由列表,将会在Switch节点前渲染Route
 * @param {Object} extraProps 添加额外的Route props
 * @param {Object} switchProps Switch props
 */
function renderRoutes(routes, authed, multipleRoutes, extraProps, switchProps) {
  let list = [];
  if (authed) authed = String(authed);
  const mapFunc = (R) =>
    R.map((route, i) => (
      <Route
        key={route.key || i}
        path={route.path}
        exact={route.exact}
        strict={route.strict}
        render={(props) => {
          // renderRoutes里写了用户权限,自动往子路由下传,方便获取当前登录权限
          route.authed = authed;

          // auth是字符串则添加数组包裹,是数组则遍历字符串化,方便下面的判断
          if (typeof route.auth === "string" || typeof route.auth === "number") {
            route.auth = [route.auth.toString()];
          } else if (Object.prototype.toString.call(route.auth) === "[object Array]") {
            route.auth.forEach((val, idx) => (route.auth[idx] = String(val)));
          }

          /**
           * 判断渲染Route
           * 1、如果路由列表没写auth则默认是可访问的
           * 2、如果renderRoute方法没传authed(当前用户权限),也是可以访问的(前提是符合1条件)
           * 3、如果即在路由列表写了权限,又添加用户权限,则判断用户权限是否存在路由权限中
           */
          if (!route.auth || authed === undefined || route.auth.includes(authed)) {
            return route.render
              ? route.render({ ...props, ...extraProps, route: route })
              : route.component && <route.component {...props} {...extraProps} route={route} />;
          } else {
            return <Redirect to={{ path: "/login", message: "请登录后再操作!" }} />;
          }
        }}
      />
    ));

  if (routes) {
    list.push(
      <Switch {...switchProps} key="SwitchRoutes">
        {mapFunc(routes)}
      </Switch>
    );
    multipleRoutes && list.unshift(...mapFunc(multipleRoutes));
    return list;
  }
}

export default renderRoutes;

修改后的routes,当匹配到"/all/article/(id值)"的路径,页面会同时渲染Article以及All两个组件,未登录则渲染Article和Login组件,mutipleRoutes主要是为了能通过多重匹配路由模拟VUE的keep-alive效果。代码如下👇:

export const mobileRouterList = [
  {
    path: "/",
    exact: true,
    render: () => <Redirect to="/all" />
  },
  {
    path: "/user",
    component: MobileMain,
    multipleRoutes: [
      // 无权限限制,当路径如:"/all/article/(id值)"时,会同时匹配"@/pages/All"
      { path: "/:page/article/:id", component: Article }
    ],
    routes: [
      {
        path: "/all",// 无权限限制页面
        component: lazy(() => import("@/pages/All"))
      },
      {
        path: "/user/me",
        auth: ["user","admin"], // 当前登录权限必须 user或admin 才可以访问
        component: lazy(() => import("@/pages/user/Me"))
      },
      {
        path: "/admin/controlPanel",
        auth: "admin", // 当前登录权限必须 admin 才可以访问
        component: lazy(() => import("@/pages/admin/ControlPanel"))
      }
    ]
  }
]

修改后的rednerRoutes使用,后面参数选填:

// 顶层使用路由渲染,第二参数<可选>:当前登录用户的权限,假设为"user",则上面的"@/pages/admin/ControlPanel"就无法访问了
renderRoutes(routes,user.authed)
// 子层组件路由渲染,第二参数若不通过props.route.authed获取顶层传的user.authed,则默认可访问该routes列表下的所有路由
renderRoutes(props.route.routes,<props.route.authed>,<props.route.multipleRoutes>,...)