[react] -- react router没有路由守卫? react-router-dom@6作者:你自己写!


路由守卫

vue-router中有beforeEnter这样的路由守卫,

可以获得从哪个路由过来的(from),要到哪个路由去(to)

是放行还是拦截(next),而react-router-dom中却没有。

老版本还有个onEnter可以用用,现在什么都没了。

因为作者说了,为了灵活性,不加入路由守卫方面的api,

也就是说,你要根据自己的需求写路由守卫。


需求

先看下我们的路由守卫都需要干些什么,

首先,我们需要知道对哪些路由进行拦截,

其次,我们也要知道从哪个路由来,到哪个路由去。

举个例子,

我想看一篇博客,于是先来到主页路由/boke?bokeId=2333,由于未登录,

路由被拦截,要跳转到/login路由去。当用户在这里登录后,

为了增加用户体验,

需要自动回到原来的路由/boke?bokeId=2333去。不然用户登录后却找不到

原来想看的博客了,那就尴尬了。


知识预备

要实现上述需求,就需要准备些知识,下面一一梳理。

Navigate跳转(重定向)

react-router-dom@6中,是使用Navigate组件进行重定向的,

而且跳转时,还可以通过state携带数据给目标路由组件。

比如

import { Navigate } from 'react-router-dom';

<Navigate to='/login' replace={true} 
state={{from: this.state.from}} />

注意,这里携带数据只能用state变量,不能用其他的,

不然在目标路由组件中获取不到。下面会讲为什么。

useLocation获取跳转携带的数据

import { useLocation } from 'react-router-dom';

const Login = () => {
 const local = useLocation();

  console.log('local: ', local) 
}

打印一下通过useLocation()得到的值可以看到:

在这里插入图片描述
local是个对象,有hash key pathname search state这五个属性,

其中,state就是跳转传递过来的数据。这也就解释了,跳转的时候,

为啥一定要用state作为参数,而不能用其他的。

class组件及react18的生命周期

为啥要了解这个呢?

因为我们要拦截路由,在页面渲染前进行判断操作,

而旧的生命周期componentWillMount将要被废弃,我们需要用新的生命周期

来处理。而这些生命周期需要在class组件中使用。

首先来看一张新的生命周期图:

在这里插入图片描述
在新的生命周期里,在render之前,有三个生命周期会执行,它们分别是:

constructor

static getDerivedStateFromProps()

shouldComponentUpdate()

在第一个里面,我们一般只定义初始状态,不做其他的操作,所以不选;

第三个,官方明确指出,不要用它来阻止页面渲染,不然可能会出现

意料之外的bug。

所以我们只能选第二个。

static getDerivedStateFromProps()该生命周期在render之前执行,有两个

参数,分别是props state,最终会return一个对象,可以是null

如果不是null,就表示状态更新了,返回的就是最新状态。

我们刚好需要更新两个状态,一个是我们是否需要往登录页跳转,

另一个是我们需要通过状态获取到跳转之前的路由。

高阶组件

高阶组件是一个函数,接收一个组件作为参数,返回的是一个新的组件。


代码实现

我把路由相关内容拆分成了3个文件,以便看起来清爽一点

ele.js用来存放路由路径,

lazySuspense.jsx用来实现路由守卫和懒加载,

index.js就是用来返回最终路由组件

先来看路由守卫:

import React, { Suspense } from 'react';
import { Navigate } from 'react-router-dom';
import { profile } from '@depjs/storage';

class RouteGuard extends React.Component {

  constructor(props) {

    super(props);

    this.state = {
      isToLoginPage: false, // 判断是否要跳转到登录页,false为不跳转
      from: '' // 从哪个路由跳转到登录页
    };
  }

  // 在页面渲染前进行拦截、判断
  static getDerivedStateFromProps(props, state) {

    const l = window.location;
    const href = l.pathname + l.search;

    // console.log('href: ', href);

    const _state = {
      isToLoginPage: false,
      from: ''
    }

	// 当前路由不是登录页的路由,并且未登录,
	// 就修改状态,跳转到登录页为true,然后给from赋值
    if (!href.includes('/login') && !(profile.userName && profile.token)) {
      _state.isToLoginPage = true;
      _state.from = href;
    }

    return _state; // 返回最新状态
  }

  render() {

    const { Com } = this.props;
    // 跳转时,使用state把from路由数据传递给登录页
    // 这里有个判断,如果要跳转到登录页,就用Navigate去跳转
    // 如果不需要跳转,就渲染对应的路由组件
    const ToCom = this.state.isToLoginPage
    ? <Navigate to='/login' replace={true} state={{from: this.state.from}} />
    : <Suspense fallback={<div>loading...</div>}>
        <Com />
      </Suspense>
    
    return (
      ToCom
    )
  }
}

// 高阶组件,用于传递数据,生成新组件
const lazySuspense = (Com) => {

  return <RouteGuard Com={Com} />
}

export default lazySuspense;

再来看ele.js

import { lazy } from 'react';
// 引入路由守卫
import lazySuspense from './lazySuspense';

import Main from '@pages/main02';
import Page404 from '@components/404';

const Login02 = lazy(() => import('@pages/login02'));
const Calendar = lazy(() => import('@pages/calendar'));
const Todos = lazy(() => import('@pages/calendar/todos'));
const CustomizeFestival = lazy(() => import('@pages/calendar/customizeFestival'));

const ele = [
  {
    path: '/',
    element: lazySuspense(Main)
  },
  {
    path: '/login',
    element: lazySuspense(Login02)
  },
  {
    path: '/calendar',
    element: lazySuspense(Calendar)
  },
  {
    path: '/todos',
    element: lazySuspense(Todos)
  },
  {
    path: '/customizeFestival',
    element: lazySuspense(CustomizeFestival)
  },
  {
    path: '*',
    element: <Page404 /> // 注意,404页面是不需要路由守卫的!
  }
]
  
export default ele;

再来看最终得到的路由组件index.js

import { useRoutes } from 'react-router-dom';
import ele from './ele';

const App = () => {

  const routes = useRoutes(ele); // 生成路由组件

  return routes;
}

export default App

最后来看看登录页Login02.jsx的处理

// 其他引入省略...
import { useLocation } from 'react-router-dom';

const Login02 = () => {
  const local = useLocation();

  console.log('local: ', local)  
}

在这里插入图片描述


项目地址

还有不清楚的地方,可以直接去看源码:

https://gitee.com/guozia007/gogo/blob/master/client/src/router/index.js
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值