react路由配置相关

一、路由配置接口设计

  • 通过统一的接口定义,确保路由配置的类型安全,便于维护和扩展。
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;
相关推荐
Hi_kenyon15 小时前
VUE3套用组件库快速开发(以Element Plus为例)三
前端·javascript·vue.js
AC赳赳老秦15 小时前
Shell 脚本批量生成:DeepSeek 辅助编写服务器运维自动化指令
运维·服务器·前端·vue.js·数据分析·自动化·deepseek
Anarkh_Lee15 小时前
别再手写 conf 了!NgxFlow:基于 React Flow 的 Nginx 可视化与调试神器
前端·nginx·数据可视化
程序员Agions15 小时前
程序员邪修手册:那些不能写进文档的骚操作
前端·后端·代码规范
jqq66615 小时前
解析ElementPlus打包源码(五、copyFiles)
前端·javascript·vue.js
Awu122715 小时前
⚡IndexedDB:现代Web应用的高性能本地数据库解决方案
前端·indexeddb
似水流年_zyh15 小时前
canvas写一个选择音频区域的组件
前端·canvas
wordbaby16 小时前
TanStack Router 实战:如何优雅地实现后台管理系统的“多页签” (TabList) 功能
前端·react.js
凌览16 小时前
2026年1月编程语言排行榜|C#拿下年度语言,Python稳居第一
前端·后端·程序员