上一篇分享了react-router v5 版本的路由管理及拦截方案,但也存在一些缺陷,例如不支持嵌套路由、不支持动态路由参数等。
后来看到了react-router v6 版本useRoutes
api 的特性,决定升级到v6版本,并对路由管理和路由拦截的实现方案进行了重构。
v6版本目前网上的文章寥寥无几,实现过程基本靠自己摸索,下面分享具体方案。
一、react-router v6
- 官方文档:https://github.com/remix-run/react-router/blob/main/docs/api.md
- 目前官方文档只有英文版,升级指南啥的网上有中文教程。
- 这里只提一下新增的一个api:
useRoutes
。
useRoutes
可以读取一个路由配置数组,生成相应的路由组件列表,类似以前的react-router-config
插件的功能,那么路由统一管理的实现用这个api就简单多了。
二、路由统一管理
1、路由配置文件
项目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: <Index />,
},
{
path: '/login',
element: <Login />,
},
{
path: '*',
element: <Page404 />,
},
]
export default routes
2、引入路由配置
(1)项目入口文件src/index.js
里引入router组件:
import { StrictMode } from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { BrowserRouter } from 'react-router-dom'
ReactDOM.render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>,
document.getElementById('root')
)
- 路由 browser / history 模式使用
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属性。
1、封装工具函数fn
新建文件src/components/RouterGuard/fn.js
:
import { Suspense, lazy } from 'react'
import { Navigate } from 'react-router-dom'
import Guard from './guard'
// 路由懒加载
export function lazyLoad (importFn, meta) {
const Element = lazy(importFn)
const lazyElement = (
<Suspense fallback={<div></div>}>
<Element _meta={meta}/>
</Suspense>
)
return <Guard element={lazyElement} meta={meta} />
}
// 路由常规加载
export function load (element, meta) {
return <Guard element={element} meta={meta} />
}
// 设置路由导航守卫函数
let handleRouteBefore = null
export function setRouteBefore (fn) {
handleRouteBefore = fn
}
export function getRouteBefore () {
return handleRouteBefore
}
// 路由重定向
export function redirect (path) {
return <Navigate to={path} />
}
- 这里的load函数作为备用,一般情况下推荐使用lazyload函数。
- lazyload函数里的fallback值就是路由切换时的loading组件,这个自行封装引用即可。
2、封装路由容器组件guard
新建组件src/components/RouterGuard/guard.jsx
:
import { Navigate, useLocation } from 'react-router-dom'
import { getRouteBefore } from './fn'
let temp = null // 用于防止重复渲染
function Guard ({ element, meta }) {
const location = useLocation()
const { pathname } = location
meta = meta || {}
const handleRouteBefore = getRouteBefore()
if (handleRouteBefore) {
if (temp === element) {
return element
}
const newPath = handleRouteBefore({ pathname, meta })
if (newPath && newPath !== pathname) {
element = <Navigate to={newPath} />
}
}
temp = element
return element
}
export default Guard
- StrictMode下组件会渲染两次,所以这里用temp处理了一下。
3、引用封装的RouterGuard
项目路由配置文件src/router/index.js
里引用:
import { lazyLoad, redirect, setRouteBefore } from '@/components/RouterGuard/fn'
// 全局路由配置
const routes = [
{
path: '/',
element: redirect('/index'),
},
{
path: '/index',
element: lazyLoad(() => import(/* webpackChunkName: "index" */ '@/views/index/index'), {
title: '首页',
needLogin: true,
}),
},
{
path: '/login',
element: lazyLoad(() => import(/* webpackChunkName: "login" */ '@/views/login/index'), {
title: '登录',
}),
},
{
path: '*',
element: lazyLoad(() => import(/* webpackChunkName: "404" */ '@/views/test/page404'), {
title: '404',
}),
},
]
/**
* @description: 全局路由拦截
* @param {string} pathname 当前路由路径
* @param {object} meta 当前路由自定义数据
* @return {string} 需要跳转到其他页时返回该页的path路径
*/
const onRouteBefore = ({ pathname, meta }) => {
// 动态修改页面title
if (meta.title !== undefined) {
document.title = meta.title
}
// 判断未登录跳转登录页
if (meta.needLogin) {
if (!isLogin) {
return '/login'
}
}
}
setRouteBefore(onRouteBefore)
export default routes
- 这里每个lazyload函数的第二个字段就是自定义的meta数据,这个数据也会以
_meta
字段名作为属性传给了每个路由组件,作为备用。
大功告成。
四、总结
实现功能:
+ 全局路由统一管理,支持便捷配置路由重定向、路由懒加载、自定义meta字段等。
+ 全局路由拦截,支持读取路由meta配置,支持拦截跳转其他路由等。
+ 同时也支持嵌套路由、动态路由参数等官方路由配置方式。