likes
comments
collection
share

探索 React Router V6 实现路由守卫的最佳实践

作者站长头像
站长
· 阅读数 39

本文由 Loui 原创,如需转载,请先私信或评论。

介绍

比起 Vue Router 可以直接使用 router.beforeEach 钩子实现路由守卫,React Router 实现路由守卫的方法似乎更加复杂、多样。接下来,请允许我介绍两种在 React Router 中实现路由守卫的方法,以供大家参考和批评。

在开始前,我准备好了一些基本的代码,如图所示:

探索 React Router V6 实现路由守卫的最佳实践

方式一:使用高阶组件包裹页面

实现步骤

  1. 封装一个 ProtectedRoute 组件,组件内根据登录情况判断返回 children 还是 <Navigate/>
    import { Navigate } from "react-router-dom"
    import { ReactNode } from "react"
    
    export default function ProtectedRoute({ children }: { children: ReactNode }) {
      const isLogged = false // 获取登录状态,这里假设为 false
      if (!isLogged) return <Navigate to="/" /> // 如果未登录,跳转到首页
      return children // 如果已登录,渲染子组件
    }
    
  2. 在路由对象中,找到要守卫的路由,用 ProtectedRoute 组件将 element 中的页面包起来
    const router = createBrowserRouter([
      {
        path: "/",
        element: <MainLayout/>,
        children:[
          {
            path:'/',
            element:<Home/>
          },
          // 找到要守卫的路由
          {
            path:'/my',
            element: <ProtectedRoute><My/></ProtectedRoute> // 使用 ProtectedRoute 包裹 My 组件
          }
        ]
      },
    ]);
    
  3. 任务完成!

优缺点

优点:

  • 简单直接,一目了然
  • 页面加载前就判断了登录情况,不会出现闪屏的情况

缺点:

  • 组件需要一个个地加上去,繁琐
  • 可扩展性不强,只能实现路由守卫一个功能

方式二:在 根页面 中监听路径

注意:这里的根页面指的是路由列表中位于第一级的组件,可能有多个,可能是一个 Layout 或者 Page,比如说我这里的根页面只有一个,就是 <MainLayout/>

实现步骤

  1. 扩展路由列表的类型声明,为路由新增一个可选的 meta 属性,之后可以通过 meta.auth 的值,来决定该路由是否需要被守卫
    // meta 里的属性类型
    interface metaType {
      auth?:boolean // auth:是否需要路由守卫进行身份验证
    }
    // 扩展 react-router-dom 的 RouteObject 类型
    declare module "react-router-dom" {
      interface IndexRouteObject {
        meta?:metaType
      }
      interface NonIndexRouteObject{
        meta?:metaType
      }
    }
    // 配置路由列表并导出
    export const routes : RouteObject[] = [
      {
        path: "/",
        element: <MainLayout/>,
        children:[
          {
            path:'/',
            element:<Home/>
          },
          {
            path:'/my',
            element: <My />,
            // 需要路由守卫,就将 meta.auth 设为 true
            meta:{
              auth:true
            },
          }
        ]
      },
    ]
    // 通过路由列表创建 router 并导出
    const router = createBrowserRouter(routes);
    export default router
    
  2. 封装一个 checkAuth Hook,用于使用在 根页面 中以进行路由守卫
    import { useEffect } from "react";
    import { matchRoutes, useLocation, useNavigate } from "react-router-dom";
    import { routes } from "../router";
    
    export default () => {
      const location = useLocation() // 获取当前位置信息
      const nav = useNavigate() // 获取路由跳转方法
      // 使用 useEffect 监听当前位置
      useEffect(()=>{
        const matches = matchRoutes(routes,location); // 使用当前位置匹配的路由
    
        const isLogged = false // 获取登录状态,这里假设为 false
    
        if(Array.isArray(matches)) {
          matches.forEach(match=>{
            // 如果 meta.auth 为 true,且未登录,则跳转到首页
            if(match.route.meta?.auth && !isLogged) {
              nav('/')
            }
          })
        } 
      },[location,nav])
    }
    
  3. 在 根页面 中,使用 checkAuth 进行路由守卫

    如果你的应用有多个 根页面,那么你应该在每个根页面都加上这个 Hook

    export default function MainLayout() {
      checkAuth() // 直接使用即可
    
      return (
        <div>
          <h1>Header</h1>
          <Outlet/>
          <h1>Footer</h1>
        </div>
      )
    }
    

优缺点

优点:

  • 可扩展性强,你可以充分利用 meta 属性,继续为它扩展类型。比如说你可以加上 title,然后在 checkAuth 里实现根据路由动态修改标题的功能

缺点:

  • 每一个 根页面 都要加上一个 Hook,繁琐
  • 由于是使用 useEffect 进行监听实现的,路由守卫会在页面挂载之后进行,可能会出现闪屏的问题

总结

React Router 中实现路由守卫的方式还有很多,由于能力有限,这篇文章中我只介绍了两种方式,远远称不上 “最佳实践”,相信一定还有更完美的做法,在这里抛砖引玉,供大家参考、批评,欢迎大家在评论区分享更好的做法。

如果这篇文章有帮到你,还请点一个不要钱的赞,感谢支持 ❤️

转载自:https://juejin.cn/post/7355670312396013618
评论
请登录