react-router 5 路由管理

实现功能:
+ 全局路由统一管理,支持配置路由重定向、路由懒加载、自定义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/

猜你喜欢

转载自blog.csdn.net/u010059669/article/details/122196916