react-router-config
Static route config helpers
使用
需要注意的是,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>,...)