实现功能:
+ 全局路由统一管理,支持配置路由重定向、路由懒加载、自定义meta字段等。
+ 全局路由拦截,支持读取路由meta配置,支持拦截跳转其他路由等。
依赖版本:
+ `[email protected]`
+ `[email protected]` // react-router v5
react-router-dom v5版本里,路由不再是js,而是一个个组件,即
。
通过path路径区分不同的路由来渲染。配合
组件只渲染内部第一个匹配到的
这个特性,来实现不同的路由地址显示对应不同的页面。
基础结构大概长这样:
<Switch>
<Route path="/index" />
<Route path="/test" />
<Route path="/demo" />
Switch>
要实现路由的统一管理,类似vue-router那样配置一个js数组来管理路由,就要考虑写一个方法来根据路由配置数组来自动生成对应的jsx dom结构。
react-router-config
插件来之前能实现类似的效果,但插件已经很久没更新了,而且插件不支持路由懒加载,所以考虑手写实现。
项目src/router/index.js
里填写路由配置:
import { lazy } from 'react'
export const routes = [
{
path: '/',
redirect: '/index', // 路由重定向字段
},
{
path: '/index',
component: lazy(() => import(/* webpackChunkName: "index" */ '@/views/index/index')),
meta: { // meta字段用来自定义路由配置
title: '首页',
},
},
{
path: '/login',
component: lazy(() => import(/* webpackChunkName: "login" */ '@/views/login/index')),
meta: {
title: '登录',
},
},
{
path: '*',
component: lazy(() => import(/* webpackChunkName: "404" */ '@/views/test/page404')),
meta: {
title: '404',
},
},
]
项目src/components/GlobalRouter/index.jsx
里封装组件引入路由配置:
import { BrowserRouter, Switch } from 'react-router-dom'
import { Suspense } from 'react'
import RouterList from './routerList'
function GlobalRouter ({ routes }) {
return (
<BrowserRouter>
<Suspense fallback={<div>{/* loading */}</div>}>
<Switch>
<RouterList routes={routes} />
</Switch>
</Suspense>
</BrowserRouter>
)
}
export default GlobalRouter
项目src/components/GlobalRouter/routerList.jsx
里封装组件渲染路由列表:
import { Route, Redirect } from 'react-router-dom'
function routerList ({ routes, location }) {
const { pathname } = location
const route404 = routes.find(v => v.path === '*') || {}
const currentRoute = routes.find(v => v.path === pathname) || route404
return currentRoute.redirect
? (<Redirect to={currentRoute.redirect} />)
: (<Route exact {...currentRoute} />)
}
export default routerList
routerList.jsx
组件是利用了这个组件在
包裹下能够通过props获取当前访问的路由location对象,后续就能依此做处理。项目入口文件src/index.js
(或根组件App.js)里配置引入封装好的组件:
import { StrictMode } from 'react'
import ReactDOM from 'react-dom'
import GlobalRouter from '@/components/GlobalRouter'
import { routes } from '@/router'
ReactDOM.render(
<StrictMode>
<GlobalRouter routes={routes} />
</StrictMode>,
document.getElementById('root')
)
这样就实现了基本的路由统一管理配置。
PS:
实现路由全局拦截,来自定义一些判断处理,类似vue里的beforeEach钩子函数,这里就在上述封装好的文件里作修改。
项目src/router/index.js
里添加内容:
/**
* @description: 全局路由拦截
* @param {object} route 当前路由配置对象
* @return {string} 需要重定向到其他页时返回该页的path路径
*/
export function onRouterBefore (route) {
const meta = route.meta || {}
// 示例:动态修改页面title
if (meta.title !== undefined) {
document.title = meta.title
}
// 示例:未登录时跳转登录页
if (!isLogin) {
return '/login'
}
}
项目src/components/GlobalRouter/routerList.jsx
里修改内容:
function routerList ({ routes, location, onRouterBefore }) {
const { pathname } = location
const route404 = routes.find(v => v.path === '*') || {}
const currentRoute = routes.find(v => v.path === pathname) || route404
const resultPath = onRouterBefore && onRouterBefore(currentRoute)
if (resultPath && resultPath !== pathname) {
return <Redirect to={resultPath} />
} else {
return currentRoute.redirect
? (<Redirect to={currentRoute.redirect} />)
: (<Route exact {...currentRoute} />)
}
}
项目src/components/GlobalRouter/index.jsx
里接收onRouterBefore:
function GlobalRouter ({ routes, onRouterBefore }) {
return (
<BrowserRouter basename={process.env.PUBLIC_URL}>
<Suspense fallback={<div>{/* loading */}</div>}>
<Switch>
<RouterList routes={routes} onRouterBefore={onRouterBefore} />
</Switch>
</Suspense>
</BrowserRouter>
)
}
项目入口文件src/index.js
里传入onRouterBefore:
import { routes, onRouterBefore } from '@/router'
ReactDOM.render(
<StrictMode>
<GlobalRouter routes={routes} onRouterBefore={onRouterBefore} />
</StrictMode>,
document.getElementById('root')
)
这样全局路由拦截方案就大概完成了。
src/router
文件夹里;src/components/GlobalRouter
组件文件夹里;目前项目中已不再使用此方案,推荐另一更完整的解决方案:react-router v6 路由统一管理及路由拦截方案
参考链接:https://www.freesion.com/article/2728789511/