实现一个react-router-dom的路由拦截

实现路由守卫需要考虑到以下的问题:

  1. 未登录情况下,访问不需要权限校验的合法页面:允许访问
  2. 登陆情况下,访问登陆页面:禁止访问,跳转至主页
  3. 登陆情况下,访问除登陆页以外的合法页面:允许访问
  4. 登陆情况下,访问所有的非法页面:禁止访问,跳转至 404
  5. 未登录情况下,访问需要权限校验的页面:禁止访问,跳转至登陆页
  6. 未登录情况下,访问所有的非法页面:禁止访问,跳转至 404

react实现路由拦截的基本思路还是利用Route 的render函数。通过判断拦截条件来实现不同的组件的跳转,从而实现拦截。
首先在跟目录src下创建一个routerMap.js文件,代码如下: 
import Index from "./page/index";
import Home from "./page/home";
import ErrorPage from "./page/error";
import Login from "./page/login";
export default [
  { path: "/", name: "App", component: Index, auth: true },
  { path: "/home", name: "Home", component: Home, auth: true },
  { path: "/login", name: "Login", component: Login },
  { path: "/404", name: "404", component: ErrorPage },
];

将 auth 设置为 true,表示该路由需要权限校验。
然后,定义 Router 组件,在App.js中,该组件是经过高阶组件包装后的结果:
//App.js
import React, { Component } from "react";
import { BrowserRouter as Router, Switch } from "react-router-dom";
import FrontendAuth from "./FrontendAuth";
import routerMap from "./routerMap";
class App extends Component {
  // eslint-disable-next-line no-useless-constructor
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <Router>
        <div>
          <Switch>
            <FrontendAuth routerConfig={routerMap} />
          </Switch>
        </div>
      </Router>
    );
  }
}
 
export default App;

所有的路由跳转,都交给 FrontendAuth 高阶组件代理完成。下面是 FrontendAuth 组件的实现:

import React, { Component } from "react";
import { Route, Redirect } from "react-router-dom";
class FrontendAuth extends Component {
  // eslint-disable-next-line no-useless-constructor
  constructor(props) {
    super(props);
  }
  render() {
    const { routerConfig, location } = this.props;
    const { pathname } = location;
    const isLogin = sessionStorage.getItem("username");
    console.log(pathname, isLogin);
    console.log(location);
    // 如果该路由不用进行权限校验,登录状态下登陆页除外
    // 因为登陆后,无法跳转到登陆页
    // 这部分代码,是为了在非登陆状态下,访问不需要权限校验的路由
    const targetRouterConfig = routerConfig.find(
      (item) => item.path === pathname
    );
    console.log(targetRouterConfig);
    if (targetRouterConfig && !targetRouterConfig.auth && !isLogin) {
      const { component } = targetRouterConfig;
      return <Route exact path={pathname} component={component} />;
    }
    if (isLogin) {
      // 如果是登陆状态,想要跳转到登陆,重定向到主页
      if (pathname === "/login") {
        return <Redirect to="/" />;
      } else {
        // 如果路由合法,就跳转到相应的路由
        if (targetRouterConfig) {
          return (
            <Route path={pathname} component={targetRouterConfig.component} />
          );
        } else {
          // 如果路由不合法,重定向到 404 页面
          return <Redirect to="/404" />;
        }
      }
    } else {
      // 非登陆状态下,当路由合法时且需要权限校验时,跳转到登陆页面,要求登陆
      if (targetRouterConfig && targetRouterConfig.auth) {
        return <Redirect to="/login" />;
      } else {
        // 非登陆状态下,路由不合法时,重定向至 404
        return <Redirect to="/404" />;
      }
    }
  }
}
export default FrontendAuth;

创建登录的界面src/page/login.js,代码如下:

import React, { Component } from "react";
import { Redirect } from "react-router-dom"; //重定向使用
class Login extends Component {
  constructor(props) {
    super(props);
    this.state = {
      username: "",
      password: "",
      rediectToReferrer: false, // 是否重定向之前的界面
    };
    this.handleChange = this.handleChange.bind(this);
    this.handleSumit = this.handleSumit.bind(this);
  }
  // 处理用户名、密码的变化
  handleChange(e) {
    if (e.target.name === "username") {
      this.setState({
        username: e.target.value,
      });
    } else if (e.target.name === "password") {
      this.setState({
        password: e.target.value,
      });
    }
  }
  // 提交登录表单
  async handleSumit(e) {
    e.preventDefault();
    const username = this.state.username;
    const password = this.state.password;
    if (username.length === 0 || password.length === 0) {
      alert("用户名或密码不能为空!");
      return;
    }
    // 保存信息到sessionStorage
    sessionStorage.setItem("username", username);
    // 登录成功后,设置redirectToReferrer为true;
    this.setState({
      rediectToReferrer: true,
    });
    let RedirectUrl = this.props.location.state
      ? this.props.location.state.from.pathname
      : "/";
    // 登陆成功之后的跳转
    this.props.history.push(RedirectUrl);
  }
  render() {
    return (
      <form className="login" onSubmit={this.handleSumit}>
        <div>
          <label htmlFor="">
            用户名:
            <input
              type="text"
              name="username"
              value={this.state.username}
              onChange={this.handleChange}
            />
          </label>
        </div>
        <div>
          <label htmlFor="">
            密码:
            <input
              type="password"
              name="password"
              value={this.state.password}
              onChange={this.handleChange}
            />
          </label>
        </div>
        <input type="submit" value="登录" />
      </form>
    );
  }
}
export default Login;

页面上的路由跳转,都由 FrontendAuth 高阶组件代理了,在 Switch 组件内部,不再是 Route 组件,而只有一个 FrontendAuth 组件。
FrontendAuth 组件接收一个名为 config 的 Props,这是一份路由表。同时,由于 FrontendAuth 组件放在了 Switch 组件内部,React Router 还自动为 FrontendAuth 注入了 location 属性,当地址栏的路由发生变化时,就会触发 location 属性对象上的 pathname 属性发生变化,从而触发 FrontendAuth 的更新(调用 render 函数)。
FrontendAuth 的 render 函数中,根据 pathname 查找到路由表中的相关配置,如果该配置中指定了无需校验,就直接返回相应的 Route 组件。
如果查找到的配置需要进行校验,再根据是否登陆进行处理,具体可以查看代码中的注释。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值