Vue 用户提问:如何在 React 中实现全局路由守卫?使用过 vue 的小伙伴都知道,如果想在 vue 中实现全局路
前言
使用过 vue 的小伙伴都知道,如果想在 vue 中实现全局路由守卫,只需要在 beforeEach 中写路由守卫逻辑即可。
但是如果使用 react 的话,应该怎么做呢?
在 react 中,其实是没有 beforeEach 的,如果需要实现路由守卫,需要结合 ReactRoute 路由库,自己手动搓一个路由守卫。
本篇文章作者会结合自己的博客管理系统所用到的路由守卫进行演示。
(文章篇幅较详细,想直接看代码的可以到最后去 clone 一份源码)
需求
我的这个管理系统对于路由守卫的要求相对简单,需求如下:
- 进入博客管理系统,判断是否登录,未登录则跳转登录页(/login)
- 如果已经登录,则跳转管理页面(/mangement)
- 如果已经登录但是不能匹配到路径,则跳转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 中实现路由守卫非常重要的一点就是:记得对可能造成死循环的路径进行判断,然后返回当前页面即可,否则使用不当可能会造成死循环。
另外路由守卫需要根据项目中的需求进行专门的 “定制” ,以上的代码可能适合我的项目,那如果你也想实现自己的路由守卫,可以借助作者的思路,希望可以帮助到你。
当然如果有觉得作者哪里写的不对的地方,大家也可以在评论区指出来,咱们一起交流进步!
以上就是关于在 react 中实现路由守卫的所有内容,如果你感觉写的还不错对你有帮助的话,可以点个赞支持一下,你的支持就是作者最大的动力 !
源码
所有代码都已经提交到 GitHub,登陆密码:666
转载自:https://juejin.cn/post/7330583100060368905