实现的功能:
下面分享具体方案。
"react": "^17.0.2",
"react-router-dom": "^6.2.1"
v6版本目前网上的文章寥寥无几,实现过程基本靠自己摸索。
useRoutes
。useRoutes
可以读取一个路由配置数组,生成相应的路由组件列表,类似以前的react-router-config
插件的功能,那么路由统一管理的实现用这个api就简单多了。实现的就是路由集中在一个文件里通过数组统一管理配置。
项目src/router/index.js
里填写路由配置:
import Index from '@/views/index/index'
import Login from '@/views/login/index'
import Page404 from '@/views/test/page404'
const routes = [
{
path: '/index',
element: ,
},
{
path: '/login',
element: ,
},
{
path: '*',
element: ,
},
]
export {
routes
}
引用路由并渲染的核心是利用react-router v6 的官方api:useRoutes
。
(1)项目入口文件src/index.js
里引入router组件:
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { BrowserRouter } from 'react-router-dom'
ReactDOM.render(
,
document.getElementById('root')
)
BrowserRouter
,hash 模式使用HashRouter
。BrowserRouter
配置basename属性。(2)项目入口组件src/App.jsx
里引入routes配置:
import { useRoutes } from 'react-router-dom'
import { routes } from '@/router'
function App () {
const elements = useRoutes(routes)
return elements
}
export default App
useRoutes
只能作用于router context中,所以useRoutes
需要写在一个子组件里被BrowserRouter
引用。实现路由全局拦截,来自定义一些判断处理。
实现思路就是控制路由配置的element属性。
新建文件src/components/RouterGuard/fn.js
:
import React from 'react'
import { Navigate } from 'react-router-dom'
import Guard from './guard'
let handleRouteBefore = null
// 设置路由导航守卫函数
function setRouteBefore (fn) {
handleRouteBefore = fn
}
// 路由懒加载
function lazyLoad (importFn, meta) {
meta = meta || {}
const Element = React.lazy(importFn)
const lazyElement = (
}>
)
return (
)
}
// 路由配置列表数据转换
function transformRoutes (routes) {
const list = []
routes.forEach(route => {
const obj = { ...route }
if (obj.redirect) {
obj.element =
}
if (obj.component) {
obj.element = lazyLoad(obj.component, obj.meta)
}
delete obj.redirect
delete obj.component
delete obj.meta
if (obj.children) {
obj.children = transformRoutes(obj.children)
}
list.push(obj)
})
return list
}
export {
setRouteBefore,
transformRoutes,
}
封装路由容器组件,就是对路由做了一个包裹,在路由渲染的时候就会执行里面的逻辑,然后调用路由拦截,做统一的路由前置钩子,既能做统一处理,也能控制拦截跳转。
新建组件src/components/RouterGuard/guard.jsx
:
import { Navigate, useLocation, useNavigate } from 'react-router-dom'
let temp = null
function Guard ({ element, meta, handleRouteBefore }) {
meta = meta || {}
const location = useLocation()
const { pathname } = location
const navigate = useNavigate()
if (handleRouteBefore) {
if (temp === element) {
return element
}
const pathRes = handleRouteBefore({ pathname, meta })
const pathResType = Object.prototype.toString.call(pathRes).match(/s(w+)]/)[1]
if (pathResType === 'Promise') {
pathRes.then(res => {
if (res && res !== pathname) {
navigate(res, { replace: true })
}
})
} else {
if (pathRes && pathRes !== pathname) {
element =
}
}
}
temp = element
return element
}
export default Guard
(1)项目路由配置文件src/router/index.js
里配置:
// 全局路由配置
const routes = [
{
path: '/',
redirect: '/index',
},
{
path: '/index',
component: () => import(/* webpackChunkName: "index" */ '@/views/index/index'),
meta: {
title: '首页',
needLogin: true,
},
},
{
path: '/login',
component: () => import(/* webpackChunkName: "login" */ '@/views/login/index'),
meta: {
title: '登录',
},
},
{
path: '*',
component: () => import(/* webpackChunkName: "404" */ '@/views/test/page404'),
meta: {
title: '404',
},
},
]
/**
* @description: 全局路由拦截
* @param {string} pathname 当前路由路径
* @param {object} meta 当前路由自定义meta字段
* @return {string} 需要跳转到其他页时,就返回一个该页的path路径,或返回resolve该路径的promise对象
*/
const onRouteBefore = ({ pathname, meta }) => {
// 动态修改页面title
if (meta.title !== undefined) {
document.title = meta.title
}
// 判断未登录跳转登录页
if (meta.needLogin) {
if (!isLogin) {
return '/login'
}
}
}
export {
routes,
onRouteBefore,
}
_meta
字段名作为属性传给了每个路由组件,以备不时之需。(2)项目入口组件src/App.jsx
里引用RouterGuard:
import { useRoutes } from 'react-router-dom'
import { routes, onRouteBefore } from '@/router'
import { transformRoutes, setRouteBefore } from '@/components/RouterGuard/fn'
function App () {
setRouteBefore(onRouteBefore)
const elements = useRoutes(transformRoutes(routes))
return elements
}
export default App
(个人搭建的react后台管理系统项目:github传送门,目前正逐步完善,路由配置就是使用的当前方案,供参考学习,欢迎star。)
路由配置文件:/src/router/index.js
import PageLayout from '@/components/Layout/index'
const routes = [
{
path: '/',
redirect: '/index',
},
{
path: '/',
element: ,
children: [
{
path: 'index',
component: () => import(/* webpackChunkName: "index" */ '@/views/index/index'),
meta: {
title: '首页',
needLogin: true,
roleId: 10000,
},
},
...... // 这里添加其他需要PageLayout页面布局的路由
]
},
...... // 这里添加不需要PageLayout布局的路由,例如登录注册页
]
页面整体布局容器组件:/src/components/PageLayout/index.jsx
import SideBar from './sideBar' // 自定义的侧边栏
import HeadBar from './headBar' // 自定义的顶部头
import { Outlet } from 'react-router-dom' // 子路由出口,类似vue的router-view
function PageLayout () {
return (
)
}
export default PageLayout
侧边栏菜单根据路由配置文件动态生成即可,需要权限判断的通过路由meta字段里自定义权限id(roleId)来匹配。