一、路由配置接口设计
- 通过统一的接口定义,确保路由配置的类型安全,便于维护和扩展。
typescript
复制代码
export interface RouteConfig {
path: string; // 路由路径
element?: React.ReactNode; // 渲染组件
children?: RouteConfig[]; // 子路由
redirect?: string; // 重定向路径
meta?: { // 路由元信息
title: string; // 页面标题
requiresAuth?: boolean; // 是否需要登录
roles?: string[]; // 允许访问的角色
};
}
二、路由分离策略
yaml
复制代码
// 公共路由(无需登录)
const publicRoutes: RouteConfig[] = [
{
path: '/login',
element: <Login />,
meta: { title: '登录', requiresAuth: false }
}
];
// 私有路由(需要登录)
const privateRoutes: RouteConfig[] = [
{
path: '/dashboard',
element: <Dashboard />,
meta: { title: '仪表板', requiresAuth: true }
}
];
三、懒加载与代码分割
React.lazy 是 React 中实现组件级代码分割的核心 API,它允许你将组件代码延迟加载,从而优化应用性能。
React.lazy必须在 Suspense 内使用
- Suspense 是 React 中用于处理异步操作的组件,它让组件可以"等待"某些操作完成,在等待期间显示回退 UI。
javascript
复制代码
// 懒加载组件定义
const LazyAbout = React.lazy(() => import('../pages/About'));
const LazyUserList = React.lazy(() => import('../pages/UserList'));
// 封装Suspense包装器组件
const LazyComponent = ({ component: Component }) => (
<Suspense fallback={<LoadingFallback />}>
<Component />
</Suspense>
);
javascript
复制代码
//此处仅示例,实际可放置全局的未加载完成时的等待样式
const LoadingFallback = () => (
<div>
<p>页面加载中...</p>
</div>
);
四、路由守卫与权限控制
- 认证守卫组件:基于token的
认证检查、自动重定向、replace防止回退到受保护的页面
javascript
复制代码
const AuthGuard = ({ children }) => {
const isAuthenticated = !!localStorage.getItem('token');
// 未认证重定向到登录页
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}
return <>{children}</>;
};
yaml
复制代码
{
path: '/user-center',
element: <UserCenter />,
meta: {
title: '用户中心',
requiresAuth: true
//权限控制
roles: ['admin']
}
}
五、递归渲染与嵌套路由
javascript
复制代码
const renderRoutes = (routes: RouteConfig[]) => {
return routes.map((route) => {
const { path, element, children, redirect, meta } = route;
// 处理重定向
if (redirect) {
return (
<Route
key={path}
path={path}
element={<Navigate to={redirect} replace />}
/>
);
}
return (
<Route
key={path}
path={path}
element={
// 根据元数据决定是否包裹AuthGuard
meta?.requiresAuth ? (
<AuthGuard>{element}</AuthGuard>
) : (
element
)
}
>
{/* 递归渲染子路由 */}
{children && renderRoutes(children)}
</Route>
);
});
};
javascript
复制代码
{
path: '/father',
children: [
{
path: '', // 访问 /father
redirect: 'son' // 重定向到 /father/son
},
{
path: 'son', // 访问 /father/son
element: <Son />
}
]
}
六、辅助工具函数
1. 面包屑导航生成
ini
复制代码
export const generateBreadcrumbs = (pathname) => {
const segments = pathname.split('/').filter(Boolean);
const breadcrumbs = [];
let currentPath = '';
for (const segment of segments) {
currentPath += `/${segment}`;
const route = findRouteByPath(currentPath);
if (route?.meta?.title) {
breadcrumbs.push({
title: route.meta.title,
path: route.path !== '/404' ? currentPath : undefined,
});
}
}
return breadcrumbs;
};
2. 路由查找功能
ini
复制代码
export const findRouteByPath = (pathname, routes = getAllRoutes()) => {
for (const route of routes) {
if (route.path === pathname) return route;
if (route.children) {
const found = findRouteByPath(pathname, route.children);
if (found) return found;
}
}
return null;
};
七、汇总使用
typescript
复制代码
// AppRouter.jsx
import React, { Suspense } from 'react';
import { Routes, Route, Navigate } from 'react-router-dom';
import App from '../pages/App';
import Father from '../pages/Father';
import Son from '../pages/Son';
import Login from '../pages/Login';
import NotFound from '../pages/NotFound';
/**
* ============================================
* 定义路由配置接口
* ============================================
*/
export interface RouteConfig {
path: string; // 路由路径
element?: React.ReactNode; // 渲染组件
children?: RouteConfig[]; // 子路由
redirect?: string; // 重定向路径
meta?: { // 路由元信息
title: string; // 页面标题
requiresAuth?: boolean; // 是否需要登录
roles?: string[]; // 允许访问的角色
};
}
/**
* 懒加载配置区域
* 使用 React.lazy 实现代码分割
*/
const LazyApp = React.lazy(() => import('../pages/App'));
const LazyFather = React.lazy(() => import('../pages/Father'));
const LazySon = React.lazy(() => import('../pages/Son'));
/**
* 加载中组件
*/
const LoadingFallback = () => (
<div>
<div>页面加载中...</div>
</div>
);
/**
* 懒加载组件包装器
*/
const LazyComponent = ({ component: Component }) => (
<Suspense fallback={<LoadingFallback />}>
<Component />
</Suspense>
);
/**
* 权限守卫组件
*/
const AuthGuard = ({ children }) => {
const isAuthenticated = !!localStorage.getItem('token');
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}
return <>{children}</>;
};
/**
* 公共路由配置(不需要登录)
*/
const publicRoutes: RouteConfig[] = [
{
path: '/login',
element: <Login />,
meta: {
title: '登录',
requiresAuth: false,
},
},
{
path: '/404',
element: <NotFound />,
meta: {
title: '页面不存在',
requiresAuth: false,
},
},
];
/**
* 私有路由配置(需要登录)
*/
const privateRoutes: RouteConfig[] = [
{
path: '/',
element: (
<AuthGuard>
<div>主布局</div> {/* 这里可以根据需要添加布局组件 */}
</AuthGuard>
),
meta: {
title: '首页',
requiresAuth: true,
},
children: [
// 默认重定向到 App 页面
{
path: '',
redirect: '/app',
},
// App 页面
{
path: 'app',
element: <LazyComponent component={LazyApp} />,
meta: {
title: 'App 页面',
requiresAuth: true,
},
},
// Father 页面
{
path: 'father',
element: <LazyComponent component={LazyFather} />,
meta: {
title: 'Father 页面',
requiresAuth: true,
},
children: [
// 访问 /father 时重定向到 /father/son
{
path: '',
redirect: 'son',
},
// Son 页面作为 Father 的子页面
{
path: 'son',
element: <LazyComponent component={LazySon} />,
meta: {
title: 'Son 页面',
requiresAuth: true,
},
},
],
},
],
},
];
/**
* 递归渲染路由配置
*/
const renderRoutes = (routes: RouteConfig[]) => {
return routes.map((route) => {
const { path, element, children, redirect, meta } = route;
// 如果有重定向,直接返回重定向路由
if (redirect) {
return (
<Route
key={path}
path={path}
element={<Navigate to={redirect} replace />}
/>
);
}
return (
<Route
key={path}
path={path}
element={
<Suspense fallback={<LoadingFallback />}>
{meta?.requiresAuth ? (
<AuthGuard>
{element}
</AuthGuard>
) : (
element
)}
</Suspense>
}
>
{/* 递归渲染子路由 */}
{children && renderRoutes(children)}
</Route>
);
});
};
/**
* 主路由组件
*/
const AppRouter = () => {
return (
<Routes>
{/* 渲染所有路由 */}
{renderRoutes([...publicRoutes, ...privateRoutes])}
{/* 处理未匹配的路由 */}
<Route path="*" element={<Navigate to="/404" replace />} />
</Routes>
);
};
/**
* 获取所有路由配置
*/
export const getAllRoutes = () => {
return [...publicRoutes, ...privateRoutes];
};
/**
* 根据路径查找路由信息
*/
export const findRouteByPath = (
pathname: string,
routes = getAllRoutes()
) => {
for (const route of routes) {
if (route.path === pathname) {
return route;
}
if (route.children) {
const found = findRouteByPath(pathname, route.children);
if (found) {
return found;
}
}
}
return null;
};
/**
* 生成面包屑导航数据
*/
export const generateBreadcrumbs = (pathname) => {
const segments = pathname.split('/').filter(Boolean);
const breadcrumbs = [];
let currentPath = '';
for (const segment of segments) {
currentPath += `/${segment}`;
const route = findRouteByPath(currentPath);
if (route?.meta?.title) {
breadcrumbs.push({
title: route.meta.title,
path: route.path !== '/404' ? currentPath : undefined,
});
}
}
return breadcrumbs;
};
export default AppRouter;