React路由高级用法:路由守卫实现方案

React路由高级用法:路由守卫实现方案

【免费下载链接】awesome-react A collection of awesome things regarding React ecosystem 【免费下载链接】awesome-react 项目地址: https://gitcode.com/GitHub_Trending/aw/awesome-react

在React应用开发中,你是否遇到过这些场景:未登录用户访问需要权限的页面、不同角色看到不同的导航菜单、页面跳转前需要保存表单数据?这些问题都可以通过路由守卫(Route Guard) 来优雅解决。本文将基于GitHub_Trending/aw/awesome-react项目中的路由生态,带你从零实现三种实用的路由守卫,让你的单页应用更安全、更智能。

路由守卫基础概念

路由守卫(Route Guard)是控制路由访问权限的中间层,在用户导航到目标路由前进行拦截验证。根据README.md中"React Routing"章节介绍,React生态中主流的路由解决方案有两个:

  • react-router(https://github.com/remix-run/react-router)- 声明式路由库
  • tanstack-router(https://github.com/TanStack/router)- 类型安全的现代路由

本文将以使用最广泛的react-router v6+为例,实现三种常见路由守卫模式:

mermaid

1. 认证守卫(Authentication Guard)

场景需求

限制未登录用户访问后台管理页面,自动重定向到登录页。

实现步骤

1.1 创建AuthGuard组件
// src/components/AuthGuard.jsx
import { Navigate, Outlet } from 'react-router-dom';

// 从本地存储或状态管理获取登录状态
const isAuthenticated = () => {
  return localStorage.getItem('token') !== null;
};

export default function AuthGuard() {
  // 未登录则重定向到登录页,并记录当前位置用于登录后返回
  if (!isAuthenticated()) {
    return <Navigate to="/login" state={{ from: window.location.pathname }} replace />;
  }
  
  // 已登录则渲染子路由
  return <Outlet />;
}
1.2 路由配置集成
// src/routes/index.jsx
import { Routes, Route } from 'react-router-dom';
import AuthGuard from '../components/AuthGuard';
import Dashboard from '../pages/Dashboard';
import Login from '../pages/Login';
import Home from '../pages/Home';

export default function AppRoutes() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/login" element={<Login />} />
      {/* 需要认证的路由组 */}
      <Route element={<AuthGuard />}>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/profile" element={<Profile />} />
        {/* 更多需要登录的路由... */}
      </Route>
    </Routes>
  );
}

核心原理

利用react-router的嵌套路由特性,将需要保护的路由作为AuthGuard的子路由。当访问子路由时,会先经过AuthGuard的验证逻辑。这种模式符合README.md中推荐的"声明式路由"最佳实践。

2. 角色权限守卫(Role-Based Guard)

场景需求

根据用户角色(如管理员、编辑、访客)限制不同功能模块的访问权限。

实现步骤

2.1 创建RoleGuard组件
// src/components/RoleGuard.jsx
import { Navigate, Outlet } from 'react-router-dom';

// 接收允许访问的角色列表作为props
export default function RoleGuard({ allowedRoles }) {
  // 实际项目中从认证系统获取用户角色
  const userRole = localStorage.getItem('role') || 'guest';
  
  if (allowedRoles.includes(userRole)) {
    return <Outlet />;
  }
  
  // 角色不匹配时重定向到403页面
  return <Navigate to="/forbidden" replace />;
}
2.2 细粒度权限控制
// src/routes/admin.jsx
import { Route } from 'react-router-dom';
import RoleGuard from '../components/RoleGuard';
import UserManagement from '../pages/admin/UserManagement';
import SystemSettings from '../pages/admin/SystemSettings';
import ContentEditor from '../pages/admin/ContentEditor';

export default function AdminRoutes() {
  return (
    <>
      {/* 只有admin角色可访问用户管理 */}
      <Route element={<RoleGuard allowedRoles={['admin']} />}>
        <Route path="user-management" element={<UserManagement />} />
      </Route>
      
      {/* editor和admin都可访问内容编辑 */}
      <Route element={<RoleGuard allowedRoles={['admin', 'editor']} />}>
        <Route path="content-editor" element={<ContentEditor />} />
      </Route>
      
      {/* 只有admin可访问系统设置 */}
      <Route element={<RoleGuard allowedRoles={['admin']} />}>
        <Route path="system-settings" element={<SystemSettings />} />
      </Route>
    </>
  );
}

权限设计建议

对于复杂权限系统,推荐结合README.md中"React State Management"章节提到的状态管理库(如Redux、Zustand)存储用户权限信息,实现更灵活的权限控制。

mermaid

3. 离开确认守卫(Leave Confirmation Guard)

场景需求

当用户在表单页面输入内容后未保存,尝试离开时弹出确认对话框。

实现步骤

3.1 创建LeaveGuard组件
// src/components/LeaveGuard.jsx
import { useNavigate, useLocation } from 'react-router-dom';
import { useEffect } from 'react';

export default function LeaveGuard({ hasUnsavedChanges, message = '您有未保存的更改,确定要离开吗?' }) {
  const navigate = useNavigate();
  const location = useLocation();

  useEffect(() => {
    const handleBeforeUnload = (event) => {
      if (hasUnsavedChanges) {
        event.preventDefault();
        event.returnValue = message; // 标准浏览器需要设置returnValue
        return message;
      }
    };

    const handleNavigate = (nextLocation) => {
      if (hasUnsavedChanges && nextLocation.pathname !== location.pathname) {
        if (!window.confirm(message)) {
          throw 'route cancelled'; // 取消导航
        }
      }
    };

    window.addEventListener('beforeunload', handleBeforeUnload);
    const unblock = navigate(blocker);

    return () => {
      window.removeEventListener('beforeunload', handleBeforeUnload);
      unblock();
    };
  }, [hasUnsavedChanges, message, navigate, location]);

  return null;
}
3.2 在表单组件中使用
// src/pages/EditProfile.jsx
import { useState } from 'react';
import LeaveGuard from '../components/LeaveGuard';

export default function EditProfile() {
  const [formData, setFormData] = useState({
    name: '',
    email: ''
  });
  const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);

  const handleInputChange = (e) => {
    setFormData({
      ...formData,
      [e.target.name]: e.target.value
    });
    setHasUnsavedChanges(true);
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    // 提交表单逻辑...
    setHasUnsavedChanges(false);
    alert('保存成功!');
  };

  return (
    <div className="edit-profile">
      {/* 离开确认守卫 - 传入未保存状态和提示信息 */}
      <LeaveGuard 
        hasUnsavedChanges={hasUnsavedChanges} 
        message="个人资料未保存,确定要离开吗?" 
      />
      
      <h2>编辑个人资料</h2>
      <form onSubmit={handleSubmit}>
        <div>
          <label>姓名:</label>
          <input 
            type="text" 
            name="name" 
            value={formData.name}
            onChange={handleInputChange}
          />
        </div>
        <div>
          <label>邮箱:</label>
          <input 
            type="email" 
            name="email" 
            value={formData.email}
            onChange={handleInputChange}
          />
        </div>
        <button type="submit">保存</button>
      </form>
    </div>
  );
}

注意事项

此守卫需注意两种场景的处理:

  1. 路由跳转(通过react-router的导航拦截)
  2. 页面刷新/关闭(通过beforeunload事件)

路由守卫最佳实践

组合使用多种守卫

实际项目中可组合使用多种守卫,例如:

// src/routes/protected-routes.jsx
<Route element={<AuthGuard />}>
  <Route path="dashboard" element={<Dashboard />} />
  
  <Route element={<RoleGuard allowedRoles={['admin']} />}>
    <Route path="admin" element={<AdminPanel />} />
  </Route>
</Route>

结合路由元信息(Meta)

对于更复杂的路由配置,可使用路由元信息定义守卫规则:

// src/routes/index.jsx
const routes = [
  {
    path: '/dashboard',
    element: <Dashboard />,
    meta: { 
      requiresAuth: true,
      roles: ['admin', 'editor'] 
    }
  }
];

然后创建一个通用的Guard组件处理元信息:

// src/components/MetaGuard.jsx
export default function MetaGuard() {
  const location = useLocation();
  const { meta } = useRouteMatch();
  
  // 根据meta信息执行相应的守卫逻辑
  if (meta?.requiresAuth && !isAuthenticated()) {
    return <Navigate to="/login" />;
  }
  
  if (meta?.roles && !meta.roles.includes(userRole)) {
    return <Navigate to="/forbidden" />;
  }
  
  return <Outlet />;
}

总结与扩展

本文基于GitHub_Trending/aw/awesome-react项目中的路由方案,实现了三种实用的路由守卫:

  1. 认证守卫:控制登录访问权限
  2. 角色守卫:基于用户角色的细粒度权限控制
  3. 离开守卫:防止用户误操作丢失数据

这些实现方案符合README.md中推荐的React最佳实践,可根据项目需求进一步扩展:

  • 结合tanstack-router实现类型安全的路由守卫
  • 使用react-querySWR在守卫中处理异步权限验证
  • 集成状态管理库实现全局守卫配置

路由守卫是React应用架构中的重要组成部分,合理使用可以显著提升应用的安全性和用户体验。建议在项目初期就规划好路由守卫策略,为后续功能扩展奠定基础。

【免费下载链接】awesome-react A collection of awesome things regarding React ecosystem 【免费下载链接】awesome-react 项目地址: https://gitcode.com/GitHub_Trending/aw/awesome-react

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值