# 路由设计文档 ## 1. 路由架构 ### 1.1 路由结构 ``` ┌─────────────────────────────────────────┐ │ App Root │ ├─────────────────────────────────────────┤ │ Router (React Router v6) │ │ ├─ /login (登录页) │ │ ├─ /403 (无权限页) │ │ ├─ /404 (404页) │ │ ├─ / (主布局) │ │ │ ├─ /dashboard (仪表盘) │ │ │ ├─ /projects (项目列表) │ │ │ ├─ /projects/create (创建项目) │ │ │ ├─ /projects/:id (项目详情) │ │ │ ├─ /projects/:id/edit (编辑项目) │ │ │ ├─ /users (用户列表) [admin] │ │ │ ├─ /users/create (创建用户) [admin] │ │ │ ├─ /users/:id (用户详情) [admin] │ │ │ └─ /statistics (统计分析) │ └─────────────────────────────────────────┘ ``` ### 1.2 路由配置 ```javascript // router/routes.js const routes = [ { path: '/login', element: , meta: { title: '登录', requiresAuth: false, hideInMenu: true, }, }, { path: '/403', element: , meta: { title: '无权限', requiresAuth: false, hideInMenu: true, }, }, { path: '/404', element: , meta: { title: '页面不存在', requiresAuth: false, hideInMenu: true, }, }, { path: '/', element: , meta: { title: '主页', requiresAuth: true, }, children: [ { path: '/dashboard', element: , meta: { title: '仪表盘', icon: 'DashboardOutlined', order: 1, }, }, { path: '/projects', element: , meta: { title: '项目管理', icon: 'FileTextOutlined', order: 2, }, }, { path: '/projects/create', element: , meta: { title: '创建项目', parent: '/projects', hideInMenu: true, }, }, { path: '/projects/:id', element: , meta: { title: '项目详情', parent: '/projects', hideInMenu: true, }, }, { path: '/projects/:id/edit', element: , meta: { title: '编辑项目', parent: '/projects', hideInMenu: true, }, }, { path: '/users', element: , meta: { title: '用户管理', icon: 'UserOutlined', order: 3, requiresRole: ['admin'], }, }, { path: '/users/create', element: , meta: { title: '创建用户', parent: '/users', requiresRole: ['admin'], hideInMenu: true, }, }, { path: '/users/:id', element: , meta: { title: '用户详情', parent: '/users', requiresRole: ['admin'], hideInMenu: true, }, }, { path: '/users/:id/edit', element: , meta: { title: '编辑用户', parent: '/users', requiresRole: ['admin'], hideInMenu: true, }, }, { path: '/statistics', element: , meta: { title: '统计分析', icon: 'BarChartOutlined', order: 4, }, }, ], }, ]; export default routes; ``` ## 2. 路由守卫 ### 2.1 PrivateRoute组件 ```javascript // router/PrivateRoute.jsx import { Navigate, useLocation } from 'react-router-dom'; import { useAuth } from '../contexts/AuthContext'; /** * 路由守卫组件 */ const PrivateRoute = ({ children, meta = {} }) => { const { isAuthenticated, user, loading } = useAuth(); const location = useLocation(); const { requiresAuth = true, requiresRole = [] } = meta; // 检查加载状态 if (loading) { return ; } // 检查认证状态 if (requiresAuth && !isAuthenticated) { return ; } // 检查角色权限 if (requiresRole.length > 0 && !requiresRole.includes(user?.role)) { return ; } return children; }; export default PrivateRoute; ``` ### 2.2 使用路由守卫 ```javascript // router/index.jsx import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; import PrivateRoute from './PrivateRoute'; import routes from './routes'; const Router = () => { return ( {routes.map((route) => { const { path, element, meta, children } = route; if (children) { return ( {element} } > {children.map((child) => ( {child.element} } /> ))} ); } return ( {element} } /> ); })} {/* 默认重定向 */} } /> {/* 404页面 */} } /> ); }; export default Router; ``` ## 3. 路由参数处理 ### 3.1 useParams Hook ```jsx import { useParams } from 'react-router-dom'; const ProjectDetail = () => { const { id } = useParams(); const { data, loading } = useApi(() => projectAPI.getDetail(id)); if (loading) return ; return
{/* 项目详情 */}
; }; ``` ### 3.2 useSearchParams Hook ```jsx import { useSearchParams } from 'react-router-dom'; import { useDebounce } from '../hooks/useDebounce'; const ProjectList = () => { const [searchParams, setSearchParams] = useSearchParams(); const keyword = searchParams.get('keyword') || ''; const debouncedKeyword = useDebounce(keyword, 300); // 搜索逻辑 useEffect(() => { fetchList({ keyword: debouncedKeyword }); }, [debouncedKeyword]); const handleSearch = (value) => { setSearchParams({ keyword: value }); }; return handleSearch(e.target.value)} />; }; ``` ### 3.3 useLocation Hook ```jsx import { useLocation } from 'react-router-dom'; const ProjectForm = () => { const location = useLocation(); const isEditMode = location.pathname.includes('/edit'); return
{isEditMode ? '编辑模式' : '创建模式'}
; }; ``` ### 3.4 useNavigate Hook ```jsx import { useNavigate } from 'react-router-dom'; const ProjectList = () => { const navigate = useNavigate(); const handleCreate = () => { navigate('/projects/create'); }; const handleEdit = (id) => { navigate(`/projects/${id}/edit`); }; const handleBack = () => { navigate(-1); // 返回上一页 }; return (
); }; ``` ## 4. 路由元信息 ### 4.1 路由元信息定义 ```javascript // router/routes.js const routeMeta = { // 页面标题 title: '项目管理', // 菜单图标 icon: 'FileTextOutlined', // 菜单排序 order: 1, // 是否需要认证 requiresAuth: true, // 允许的角色 requiresRole: ['admin', 'market'], // 父路由 parent: '/projects', // 是否在菜单中隐藏 hideInMenu: false, // 是否在面包屑中隐藏 hideInBreadcrumb: false, // 缓存配置 keepAlive: false, }; ``` ### 4.2 使用路由元信息 ```javascript // hooks/useRouteMeta.js import { useLocation, useParams } from 'react-router-dom'; import routes from '../router/routes'; /** * 路由元信息Hook */ export const useRouteMeta = () => { const location = useLocation(); const params = useParams(); // 查找当前路由配置 const findRouteMeta = (path, routeList) => { for (const route of routeList) { // 精确匹配 if (route.path === path) { return route.meta; } // 参数匹配 const routePattern = route.path.replace(/:[^/]+/g, '[^/]+'); const regex = new RegExp(`^${routePattern}$`); if (regex.test(path)) { return route.meta; } // 递归查找子路由 if (route.children) { const childMeta = findRouteMeta(path, route.children); if (childMeta) return childMeta; } } return {}; }; const meta = findRouteMeta(location.pathname, routes); return { meta, pathname: location.pathname, search: location.search, params, }; }; ``` ### 4.3 动态设置页面标题 ```javascript // App.jsx import { useEffect } from 'react'; import { useLocation } from 'react-router-dom'; import { useRouteMeta } from './hooks/useRouteMeta'; const App = () => { const { meta } = useRouteMeta(); useEffect(() => { if (meta.title) { document.title = `${meta.title} - 海洋项目管理系统`; } }, [meta.title]); return ; }; ``` ## 5. 菜单生成 ### 5.1 根据路由生成菜单 ```javascript // hooks/useMenu.js import { useMemo } from 'react'; import { useAuth } from './useAuth'; import { useRouteMeta } from './useRouteMeta'; import routes from '../router/routes'; /** * 生成菜单 */ export const useMenu = () => { const { hasRole } = useAuth(); const { pathname } = useRouteMeta(); const menuItems = useMemo(() => { const generateMenu = (routeList) => { return routeList .filter((route) => { // 过滤隐藏的菜单项 if (route.meta?.hideInMenu) return false; // 过滤需要权限的菜单项 if (route.meta?.requiresRole?.length > 0) { return hasRole(route.meta.requiresRole); } return true; }) .map((route) => { if (route.children) { return { key: route.path, icon: route.meta?.icon, label: route.meta?.title, children: generateMenu(route.children), }; } return { key: route.path, icon: route.meta?.icon, label: route.meta?.title, }; }); }; return generateMenu(routes); }, [hasRole]); const selectedKeys = useMemo(() => { return [pathname]; }, [pathname]); const openKeys = useMemo(() => { // 根据当前路径展开子菜单 const paths = pathname.split('/').filter(Boolean); const keys = paths.map((_, index) => `/${paths.slice(0, index + 1).join('/')}`); return keys; }, [pathname]); return { menuItems, selectedKeys, openKeys, }; }; ``` ### 5.2 侧边栏菜单 ```jsx // components/Layout/Sidebar.jsx import { Menu } from 'antd'; import { useNavigate, useLocation } from 'react-router-dom'; import { useMenu } from '../../hooks/useMenu'; const Sidebar = () => { const navigate = useNavigate(); const { menuItems, selectedKeys, openKeys } = useMenu(); const handleMenuClick = ({ key }) => { navigate(key); }; return (
Logo 海洋项目管理系统
); }; ``` ## 6. 面包屑导航 ### 6.1 面包屑生成 ```javascript // hooks/useBreadcrumb.js import { useMemo } from 'react'; import { useLocation, useMatches } from 'react-router-dom'; import routes from '../router/routes'; /** * 生成面包屑 */ export const useBreadcrumb = () => { const location = useLocation(); const breadcrumbItems = useMemo(() => { const items = []; // 查找当前路径的所有路由 const pathSegments = location.pathname.split('/').filter(Boolean); let currentPath = ''; for (const segment of pathSegments) { currentPath += `/${segment}`; // 查找路由配置 const findRoute = (routeList, path) => { for (const route of routeList) { if (route.path === path) { return route; } if (route.children) { const found = findRoute(route.children, path); if (found) return found; } } return null; }; const route = findRoute(routes, currentPath); if (route && !route.meta?.hideInBreadcrumb) { items.push({ path: currentPath, title: route.meta?.title || segment, }); } } return items; }, [location.pathname]); return { breadcrumbItems }; }; ``` ### 6.2 面包屑组件 ```jsx // components/Common/Breadcrumb.jsx import { Breadcrumb } from 'antd'; import { useNavigate } from 'react-router-dom'; import { useBreadcrumb } from '../../hooks/useBreadcrumb'; const BreadcrumbNav = () => { const navigate = useNavigate(); const { breadcrumbItems } = useBreadcrumb(); const handleBreadcrumbClick = (path) => { navigate(path); }; return ( navigate('/home')}>首页 {breadcrumbItems.map((item, index) => ( handleBreadcrumbClick(item.path)} > {item.title} ))} ); }; ``` ## 7. 路由过渡动画 ### 7.1 路由过渡组件 ```jsx // components/RouterTransition.jsx import { useLocation } from 'react-router-dom'; import { CSSTransition, SwitchTransition } from 'react-transition-group'; import './RouterTransition.css'; const RouterTransition = ({ children }) => { const location = useLocation(); return ( {children} ); }; ``` ### 7.2 过渡动画样式 ```css /* RouterTransition.css */ .fade-enter { opacity: 0; transform: translateX(20px); } .fade-enter-active { opacity: 1; transform: translateX(0); transition: opacity 300ms, transform 300ms; } .fade-exit { opacity: 1; transform: translateX(0); } .fade-exit-active { opacity: 0; transform: translateX(-20px); transition: opacity 300ms, transform 300ms; } ``` ## 8. 路由缓存 ### 8.1 KeepAlive组件 ```jsx // hooks/useKeepAlive.js import { useState, useRef } from 'react'; const keepAliveCache = new Map(); export const useKeepAlive = (cacheKey, children) => { const [isAlive, setIsAlive] = useState(false); const activate = () => setIsAlive(true); const deactivate = () => setIsAlive(false); if (!keepAliveCache.has(cacheKey)) { keepAliveCache.set(cacheKey, { children, isActive: false }); } const cache = keepAliveCache.get(cacheKey); return { isAlive, activate, deactivate, cachedChildren: cache.children, }; }; ``` ### 8.2 使用KeepAlive ```jsx const CachedComponent = () => { const { cachedChildren } = useKeepAlive('dashboard', ); return cachedChildren; }; ``` ## 9. 路由权限控制 ### 9.1 权限指令 ```jsx // components/Permission.jsx import { useAuth } from '../contexts/AuthContext'; /** * 权限控制组件 */ const Permission = ({ role, permission, children, fallback = null }) => { const { user, hasRole, hasPermission } = useAuth(); // 检查角色权限 if (role && !hasRole(role)) { return fallback; } // 检查功能权限 if (permission && !hasPermission(permission)) { return fallback; } return children; }; export default Permission; ``` ### 9.2 使用权限组件 ```jsx import Permission from './Permission'; const ProjectList = () => { return (
{/* 只有管理员可见 */} {/* 只有市场部用户可见 */} {/* 没有权限时显示备用内容 */} 无权限}>
); }; ``` ## 10. 路由最佳实践 ### 10.1 路由命名规范 ```javascript // 好的路由命名 '/projects' // 项目列表 '/projects/create' // 创建项目 '/projects/:id' // 项目详情 '/projects/:id/edit' // 编辑项目 // 不好的路由命名 '/p' // 太短,不清晰 '/project-list-page' // 太长,冗余 '/projects/1/edit' // 使用硬编码ID ``` ### 10.2 路由嵌套 ```javascript // 合理的路由嵌套 }> } /> } /> } /> } /> // 不合理的路由嵌套(过深) }> }> }> } /> ``` ### 10.3 路由懒加载 ```javascript // 懒加载路由组件 import { lazy, Suspense } from 'react'; const ProjectList = lazy(() => import('./pages/Projects/ProjectList')); const ProjectDetail = lazy(() => import('./pages/Projects/ProjectDetail')); const Router = () => { return ( }> } /> } /> ); }; ``` --- **文档维护**: 前端程序员 **文档类型**: 路由设计文档 **最后更新**: 2026-01-25