0
点赞
收藏
分享

微信扫一扫

react-router v6 路由管理、路由拦截方案

上一篇分享了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配置,支持拦截跳转其他路由等。
+ 同时也支持嵌套路由、动态路由参数等官方路由配置方式。
举报

相关推荐

0 条评论