实现功能:
+ 全局路由统一管理,支持配置路由重定向、路由懒加载、自定义meta字段等。
+ 全局路由拦截,支持读取路由meta配置,支持拦截跳转其他路由等。
依赖版本:
+ `[email protected]`
+ `[email protected]` // react-router v5
一、react路由
react-router-dom v5版本里,路由不再是js,而是一个个组件,即<Route />
。
通过path路径区分不同的路由来渲染。配合<Switch />
组件只渲染内部第一个匹配到的<Route />
这个特性,来实现不同的路由地址显示对应不同的页面。
基础结构大概长这样:
<Switch>
<Route path="/index" />
<Route path="/test" />
<Route path="/demo" />
</Switch>
二、路由统一管理
要实现路由的统一管理,类似vue-router那样配置一个js数组来管理路由,就要考虑写一个方法来根据路由配置数组来自动生成对应的jsx dom结构。
react-router-config
插件来之前能实现类似的效果,但插件已经很久没更新了,而且插件不支持路由懒加载,所以考虑手写实现。
1、路由配置文件
项目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',
},
},
]
2、封装路由组件
项目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
组件是利用了这个组件在<Switch>
包裹下能够通过props获取当前访问的路由location对象,后续就能依此做处理。 - 路由全部为精确匹配。
3、引入路由组件
项目入口文件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:
- 我这里是使用的路由history模式,如果是路由hash模式就把BrowserRouter替换成HashRouter。
- 如果项目是部署在服务器域名的子目录下,就给BrowserRouter添加basename属性,值为子目录路径,例如/h5。
三、全局路由拦截
实现路由全局拦截,来自定义一些判断处理,类似vue里的beforeEach钩子函数,这里就在上述封装好的文件里作修改。
1、路由拦截函数
项目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'
}
}
- 这里定的规则就是如果需要拦截跳转其他页,onRouterBefore就return一个返回值,值为要跳转的页面path路径,不需要跳转就不用return。
2、处理拦截函数
项目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')
)
这样全局路由拦截方案就大概完成了。
四、思考
1、插件化处理
- 配置文件都在
src/router
文件夹里; - 实现逻辑都在
src/components/GlobalRouter
组件文件夹里; - 配置文件与实现逻辑的解耦,方便用于多项目共享。
2、待优化点
- 暂未支持嵌套路由。
目前项目中已不再使用此方案,推荐另一更完整的解决方案:react-router v6 路由统一管理及路由拦截方案
参考链接:https://www.freesion.com/article/2728789511/