探索 React Router V6 实现路由守卫的最佳实践
本文由 Loui 原创,如需转载,请先私信或评论。
介绍
比起 Vue Router 可以直接使用 router.beforeEach
钩子实现路由守卫,React Router 实现路由守卫的方法似乎更加复杂、多样。接下来,请允许我介绍两种在 React Router 中实现路由守卫的方法,以供大家参考和批评。
在开始前,我准备好了一些基本的代码,如图所示:
方式一:使用高阶组件包裹页面
实现步骤
- 封装一个 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 // 如果已登录,渲染子组件 }
- 在路由对象中,找到要守卫的路由,用 ProtectedRoute 组件将 element 中的页面包起来
const router = createBrowserRouter([ { path: "/", element: <MainLayout/>, children:[ { path:'/', element:<Home/> }, // 找到要守卫的路由 { path:'/my', element: <ProtectedRoute><My/></ProtectedRoute> // 使用 ProtectedRoute 包裹 My 组件 } ] }, ]);
- 任务完成!
优缺点
优点:
- 简单直接,一目了然
- 页面加载前就判断了登录情况,不会出现闪屏的情况
缺点:
- 组件需要一个个地加上去,繁琐
- 可扩展性不强,只能实现路由守卫一个功能
方式二:在 根页面 中监听路径
注意:这里的根页面指的是路由列表中位于第一级的组件,可能有多个,可能是一个 Layout 或者 Page,比如说我这里的根页面只有一个,就是
<MainLayout/>
实现步骤
- 扩展路由列表的类型声明,为路由新增一个可选的 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
- 封装一个 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]) }
- 在 根页面 中,使用 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