Vue 用户提问:如何在 React 中实现全局路由守卫?

前言

如果想在 vue 中实现全局路由守卫,只需要在 beforeEach 中写路由守卫逻辑即可。

但是如果使用 react 的话,应该怎么做呢?

在 react 中,其实是没有 beforeEach 的,如果需要实现路由守卫,需要结合  ReactRoute  路由库,自己手动搓一个路由守卫。

需求

我的这个管理系统对于路由守卫的要求相对简单,需求如下:

  1. 进入博客管理系统,判断是否登录,未登录则跳转登录页(/login)
  2. 如果已经登录,则跳转管理页面(/mangement)
  3. 如果已经登录但是不能匹配到路径,则跳转 404 页面(/404)

最主要的功能还是 1 和 2,如果你已经明白需求了,那就继续往下看吧!

实现

基本路由

如果不使用路由守卫,如何在 react 中实现路由?

文档如下:

  我们先按着文档实现一个基本的路由格式,在 app.tsx 中实现如下:

//app.tsx
import React from "react";
import { BrowserRouter as Router } from "react-router-dom";
import GetRouter from "./components/getRouter";

const App = () => {
  return (
    <Router>
      <GetRouter />
    </Router>
  );
};

export default App;

我简单的把路由信息进行了封装,下面是 GetRouter 文件:

//./components/getRouter
import { useRoutes } from "react-router-dom";
import routes from "../router";

export default function GetRouter() {
  let element = useRoutes(routes);
  return element;
}

routes 文件如下:

// ../router
import { lazy } from "react";
const Management = lazy(() => import("../views/management"));
const Login = lazy(() => import("../views/login"));
const Error = lazy(() => import("../views/404"));
const About = lazy(() => import("../views/about"));

const routes = [
  {
    path: "/",
    element: <Login />,
  },
  {
    path: "/login",
    element: <Login />,
  },
  {
    path: "/management",
    element: <Management />,
  },
  {
    path: "/404",
    element: <Error />,
  },
  {
    path: "/about",
    element: <About />,
  },
];

export default routes;

这样一个简单的路由就实现了

增加路由守卫

现在我们开始增加路由守卫,只需要把我们的路由信息传入到我们路由守卫组件中即可。

那接下来我们就着重分析一下   UseAuth 这个组件!

路由守卫组件

第一个需求

进入博客管理系统,判断是否登录,未登录则跳转登录页(/login)

先实现一个简单的逻辑,如果没有登录就重定向到登陆页面,我们从本地存储里面去取登录信息进行判断,不成立跳转到登陆页面,那么这样看似是可以的。

import React from "react";
import { Navigate, useLocation } from "react-router-dom";
export default function AuthRoute(props: { children: JSX.Element }) {
  const isLogin = localStorage.getItem("isLogin");
  if (!isLogin) {
    return <Navigate to="/login" />;
  }
}

但是,我们这样写了之后,会发现并没有重定向到登录页面,反而造成了程序死循环!

为什么呢?

可以这样理解,如果每次没有登录都跳转到登录页面,那跳转登录页之前,会再次进入路由守卫,路由守卫再次判断仍然没有登录,就这样循环下去,变成了死循环。。。。

怎么解决?

只需要判断是否登录,如果没有登录,返回当前页面即可。

但是又因为我们的链接默认打开是根路径 '/',所以这里需要加一个判断,如果没有登录并且当前是登录页才返回当前页面。

import React from "react";
import { Navigate, useLocation } from "react-router-dom";
export default function AuthRoute(props: { children: JSX.Element }) {
  const location = useLocation();
  const isLogin = localStorage.getItem("isLogin");
  const currentPath = location.pathname;

  const condition = currentPath === "/login" && !isLogin;

  if (condition) {
    return props.children;
  }

  if (!isLogin) {
    return <Navigate to="/login" />;
  }
  return props.children;
}

现在可能有些朋友感觉迷了,我们再来理一下流程:

首先项目启动打开链接  http://localhost:8081

默认进入为 '/' 根路径

判断没有登录,重定向到登录页

此时为登录页,又进入路由守卫,仍然没有登录

此时路径为登录页,并且没有登录,条件成立,返回当前页面,避免死循环

最后函数需要无条件返回 props.children,也就是当前页面,要不然会报这个错误

可以看到我们直接打开链接就进入了登陆页面 login

第二个需求

如果已经登录,则跳转管理页面(/mangement)

如果前面都实现了这个就好做了,只需要判断是否登录,如果登录就重定向到登录页。

但是还是需要判断,如果当前为管理页面,并且已经登录就返回当前页面,防止一直路由守卫判断。

import React from "react";
import { Navigate, useLocation } from "react-router-dom";
export default function AuthRoute(props: { children: JSX.Element }) {
  const location = useLocation();
  const isLogin = localStorage.getItem("isLogin");
  const currentPath = location.pathname;

  const condition =
    (currentPath === "/login" && !isLogin) ||
    (currentPath === "/management" && isLogin);

  if (condition) {
    return props.children;
  }

  if (!isLogin) {
    return <Navigate to="/login" />;
  }

  if (isLogin) {
    return <Navigate to="/management" />;
  }
  return props.children;
}

第三个需求

如果已经登录但是不能匹配到路径,则跳转 404 页面(/404)

如果我在地址栏输入例如 /aaa 这样的路径,我本地是匹配不到的,但是这时已经登陆了,这个时候我希望可以跳转到 404 页面。

我们现在需要增加一个匹配路径功能。代码如下:

我们封装一个匹配路径函数,如果匹配不到则直接返回即可。

import routes from "../router";
function matchRoute(path: String) {
  const findPath = routes.find((val) => val.path === path);
  if (!findPath) {
    return;
  }
  return findPath;
}
export default matchRoute;

引入 matchAuth  匹配函数后代码如下,如果没有匹配到则返回 404 页面,同样上面需要增加 404 页面的判断,否则会死循环。

import React from "react";
import matchAuth from "../utils/matchAuth";
import { Navigate, useLocation } from "react-router-dom";
export default function AuthRoute(props: { children: JSX.Element }) {
  const location = useLocation();
  const isLogin = localStorage.getItem("isLogin");
  const condition =
    (currentPath === "/login" && !isLogin) ||
    (currentPath === "/management" && isLogin) ||
    (currentPath === "/404" && isLogin);

  const findPath = matchAuth(currentPath);

  if (condition) {
    return props.children;
  }

  if (!findPath && isLogin) {
    return <Navigate to="/404" />;
  }

  if (!isLogin) {
    return <Navigate to="/login" />;
  }

  if (isLogin) {
    return <Navigate to="/management" />;
  }

  return props.children;
}

总结

在 react 中实现路由守卫和 vue 不同,vue 可以使用内置的前置路由守卫函数,react 需要自己实现,不过原理大致相同。

在 react 中实现路由守卫非常重要的一点就是:记得对可能造成死循环的路径进行判断,然后返回当前页面即可,否则使用不当可能会造成死循环。

posted @ 2024-02-04 15:09  柯基与佩奇  阅读(173)  评论(0编辑  收藏  举报