React(react-router-dom) 根据路径自动配置路由 延申
// app.js
import './App.css';
import { BrowserRouter as Router, Route, Redirect } from 'react-router-dom'
// 将 routerHandle 抽离
import { routerHandle } from './utils/common.tsx'
// 通过正则排除无需创建路由文件夹,这里排除的是 components 下包含的文件
const routers = require.context('./pages/', true, /^(?!.*\/(components)\/).*\.(tsx|jsx|js)$/)
function App() {
return (
<Router>
<div className="App">
{/* 默认路由匹配时,跳转到 /home 实现路由重定向到首页 */}
<Route path="/" exact render={() => <Redirect to="/home" />} />
{/* 路由 */}
{routerHandle(routers)}
</div>
</Router>
);
}
export default App;
// common.tsx
import { Route } from 'react-router-dom'
const COMPONENT_DIR = 'components/'
// 对比上一版,暂时先不做 toLowerCase() 处理
export function pathHandle(key: string) {
let path = key
.replace(/^\./, '')
.replace(/\.(tsx|jsx|js)$/, '')
.replace(/\/index(\[\w+])*$/, '$1')
.replace(/\[(\w+)]/g, '/:$1')
.replace(/\/\//g, '/')
return path
}
function makeRouterMap(routers: any) {
let routerMap = {} as any
let keys = routers.keys()
for (let key of keys) {
let path = pathHandle(key)
// 新增功能,配置自定义路由组件,通过使用文件夹 /(组件名称)/index.tsx 的方式标记文件,使用组件处理路由
let Route = null
// 判断是否包含括号
if (path.indexOf('(') !== -1 && path.indexOf(')') !== -1) {
// 取出组件名称
let myRouteName = path.slice(path.indexOf('(') + 1, path.indexOf(')'))
// 规定组件默认保存在 src/components 下
Route = require('../' + COMPONENT_DIR + myRouteName)
}
// 去掉 path 的组件文件夹
path = path.replace(/(\/\((\w+)\))/, '')
routerMap[path] = {
path,
children: [],
isTop: true,
key,
Route: Route?.default // 加入组件函数
}
}
return routerMap
}
export function routerHandle(routers: any) {
let routerMap = makeRouterMap(routers)
let routerMapKeys = Object.keys(routerMap)
routerMapKeys.forEach(key => {
let parentPath = key.replace(/\/:\w+$/, '').replace(/\/\w+$/, '')
let parent = routerMap[parentPath]
if (parent) {
routerMap[key].isTop = false
parent.children.push(routerMap[key])
}
})
let topRouters = [] as any[]
routerMapKeys.forEach(key => {
let route = routerMap[key]
if (route.isTop) {
topRouters.push(route)
}
})
return childrenRouteHandler(topRouters, routers)
}
export function childrenRouteHandler(items: any, routers: any) {
return items.map((Item: any) => {
let path = Item.path.toLowerCase()
if (Item.children.length > 0) {
// 循环时判断是否含有,自定义组件,有则使用自定义组件
if (Item.Route) {
return <Item.Route
key={path}
path={`${path}`}
component={
routers(Item.key).default.bind({
childrenRoute: childrenRouteHandler(Item.children, routers)
})
}>
</Item.Route>
}
return <Route
key={path}
path={`${path}`}
component={
routers(Item.key).default.bind({
childrenRoute: childrenRouteHandler(Item.children, routers)
})
}>
</Route>
} else {
if (Item.Route) {
return <Item.Route
key={path}
exact
path={`${path.replace(/\/default$/, '')}`}
component={routers(Item.key).default} >
</Item.Route>
}
return <Route
key={path}
exact
path={`${path.replace(/\/default$/, '')}`}
component={routers(Item.key).default} >
</Route>
}
})
}
// src/components/AutoRoute/index.tsx
// 权限判断路由,判断用户是否登录,登录返回页面,未登录则跳到登录页面
import { Redirect, Route } from "react-router"
// 是否登录判断方法
import { isAuth } from "../../utils"
export default function AutoRoute({ component: Component, ...rest }: any) {
// 注意,组件参数应, 完全按照 Route 保障统一
return <Route
{...rest}
render={(props: any) => {
return isAuth() ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
}
/>
}
// 配置文件路径, 例如,src/page/home/(AutoRoute)/test.tsx
// 生成路径 /home/test 同时使用 AutoRoute 包裹
补充
React(react-router-dom) 根据路径自动配置路由,第一版
React(react-router-dom) 根据路径自动配置路由,第三版
注意
嵌套子路由,包含在父级的 this 中,使用时务必,将其渲染,否则无效
配置方式针对函数组件
父级使用了路由组件会影响到子级,这点在第三版有处理