feat: route/index 重构
This commit is contained in:
53
src/routes/components/RouteContainer.js
Normal file
53
src/routes/components/RouteContainer.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
// src/routes/components/RouteContainer.js
|
||||||
|
// 路由容器组件 - 提供统一的错误边界、加载状态和主题背景
|
||||||
|
|
||||||
|
import React, { Suspense } from 'react';
|
||||||
|
import { Box, useColorMode } from '@chakra-ui/react';
|
||||||
|
import ErrorBoundary from '@components/ErrorBoundary';
|
||||||
|
import PageLoader from '@components/Loading/PageLoader';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RouteContainer - 路由容器组件
|
||||||
|
*
|
||||||
|
* 为路由系统提供统一的外层包装,包含:
|
||||||
|
* 1. 主题感知的背景色(深色/浅色模式)
|
||||||
|
* 2. Suspense 懒加载边界(显示加载提示)
|
||||||
|
* 3. ErrorBoundary 错误边界(隔离路由错误)
|
||||||
|
*
|
||||||
|
* 这个组件确保:
|
||||||
|
* - 所有路由页面都有一致的背景色
|
||||||
|
* - 懒加载组件有统一的加载提示
|
||||||
|
* - 单个路由的错误不会导致整个应用崩溃
|
||||||
|
*
|
||||||
|
* @param {Object} props
|
||||||
|
* @param {React.ReactNode} props.children - 子组件(通常是 Routes)
|
||||||
|
* @param {string} [props.loadingMessage='加载页面中...'] - 加载提示文本
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* <RouteContainer>
|
||||||
|
* <Routes>
|
||||||
|
* <Route path="/" element={<Home />} />
|
||||||
|
* </Routes>
|
||||||
|
* </RouteContainer>
|
||||||
|
*/
|
||||||
|
export function RouteContainer({
|
||||||
|
children,
|
||||||
|
loadingMessage = "加载页面中..."
|
||||||
|
}) {
|
||||||
|
const { colorMode } = useColorMode();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
minH="100vh"
|
||||||
|
bg={colorMode === 'dark' ? 'gray.800' : 'white'}
|
||||||
|
>
|
||||||
|
{/* Suspense 统一处理懒加载组件的加载状态 */}
|
||||||
|
<Suspense fallback={<PageLoader message={loadingMessage} />}>
|
||||||
|
{/* ErrorBoundary 隔离路由错误,防止整个应用崩溃 */}
|
||||||
|
<ErrorBoundary>
|
||||||
|
{children}
|
||||||
|
</ErrorBoundary>
|
||||||
|
</Suspense>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
4
src/routes/components/index.js
Normal file
4
src/routes/components/index.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// src/routes/components/index.js
|
||||||
|
// 统一导出所有路由组件
|
||||||
|
|
||||||
|
export { RouteContainer } from './RouteContainer';
|
||||||
5
src/routes/constants/index.js
Normal file
5
src/routes/constants/index.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// src/routes/constants/index.js
|
||||||
|
// 统一导出所有路由常量
|
||||||
|
|
||||||
|
export { LAYOUT_COMPONENTS } from './layoutComponents';
|
||||||
|
export { PROTECTION_WRAPPER_MAP } from './protectionWrappers';
|
||||||
26
src/routes/constants/layoutComponents.js
Normal file
26
src/routes/constants/layoutComponents.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// src/routes/constants/layoutComponents.js
|
||||||
|
// 布局组件映射表
|
||||||
|
|
||||||
|
import Auth from '@layouts/Auth';
|
||||||
|
import HomeLayout from '@layouts/Home';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 特殊布局组件映射表
|
||||||
|
*
|
||||||
|
* 用于将字符串标识符映射到实际的组件。
|
||||||
|
* 这些是非懒加载的布局组件,在 routeConfig.js 中通过字符串引用。
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // 在 routeConfig.js 中:
|
||||||
|
* {
|
||||||
|
* path: 'auth/*',
|
||||||
|
* component: 'Auth', // 字符串标识符
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // 通过 LAYOUT_COMPONENTS['Auth'] 获取实际组件
|
||||||
|
*/
|
||||||
|
export const LAYOUT_COMPONENTS = {
|
||||||
|
Auth,
|
||||||
|
HomeLayout,
|
||||||
|
};
|
||||||
24
src/routes/constants/protectionWrappers.js
Normal file
24
src/routes/constants/protectionWrappers.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// src/routes/constants/protectionWrappers.js
|
||||||
|
// 路由保护包装器映射表
|
||||||
|
|
||||||
|
import ProtectedRoute from '@components/ProtectedRoute';
|
||||||
|
import ProtectedRouteRedirect from '@components/ProtectedRouteRedirect';
|
||||||
|
import { PROTECTION_MODES } from '../routeConfig';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保护模式包装器映射表
|
||||||
|
*
|
||||||
|
* 根据路由的保护模式选择对应的保护组件。
|
||||||
|
* 支持以下保护模式:
|
||||||
|
* - MODAL: 弹窗登录模式 (ProtectedRoute)
|
||||||
|
* - REDIRECT: 跳转登录模式 (ProtectedRouteRedirect)
|
||||||
|
* - PUBLIC: 公开访问,无保护 (无包装器)
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const WrapperComponent = PROTECTION_WRAPPER_MAP[PROTECTION_MODES.MODAL];
|
||||||
|
* // 返回 ProtectedRoute 组件
|
||||||
|
*/
|
||||||
|
export const PROTECTION_WRAPPER_MAP = {
|
||||||
|
[PROTECTION_MODES.MODAL]: ProtectedRoute,
|
||||||
|
[PROTECTION_MODES.REDIRECT]: ProtectedRouteRedirect,
|
||||||
|
};
|
||||||
50
src/routes/utils/renderRoute.js
Normal file
50
src/routes/utils/renderRoute.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
// src/routes/utils/renderRoute.js
|
||||||
|
// 路由渲染工具函数
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { Route } from 'react-router-dom';
|
||||||
|
import { LAYOUT_COMPONENTS } from '../constants';
|
||||||
|
import { wrapWithProtection } from './wrapWithProtection';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染单个路由
|
||||||
|
*
|
||||||
|
* 根据路由配置项生成 React Router 的 Route 组件。
|
||||||
|
* 处理以下逻辑:
|
||||||
|
* 1. 解析组件(特殊布局组件 vs 懒加载组件)
|
||||||
|
* 2. 应用路由保护(根据 protection 字段)
|
||||||
|
* 3. 生成唯一 key
|
||||||
|
*
|
||||||
|
* @param {Object} routeItem - 路由配置项(来自 routeConfig.js)
|
||||||
|
* @param {string} routeItem.path - 路由路径
|
||||||
|
* @param {React.ComponentType|string} routeItem.component - 组件或组件标识符
|
||||||
|
* @param {string} routeItem.protection - 保护模式 (modal/redirect/public)
|
||||||
|
* @param {number} index - 路由索引,用于生成唯一 key
|
||||||
|
*
|
||||||
|
* @returns {React.ReactElement} Route 组件
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // 使用示例
|
||||||
|
* const routes = [
|
||||||
|
* { path: 'community', component: CommunityComponent, protection: 'modal' },
|
||||||
|
* { path: 'auth/*', component: 'Auth', protection: 'public' },
|
||||||
|
* ];
|
||||||
|
*
|
||||||
|
* routes.map((route, index) => renderRoute(route, index));
|
||||||
|
*/
|
||||||
|
export function renderRoute(routeItem, index) {
|
||||||
|
const { path, component, protection } = routeItem;
|
||||||
|
|
||||||
|
// 解析组件:
|
||||||
|
// - 如果是字符串(如 'Auth', 'HomeLayout'),从 LAYOUT_COMPONENTS 映射表查找
|
||||||
|
// - 否则直接使用(懒加载组件)
|
||||||
|
const Component = LAYOUT_COMPONENTS[component] || component;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Route
|
||||||
|
key={`${path}-${index}`}
|
||||||
|
path={path}
|
||||||
|
element={wrapWithProtection(Component, protection)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
44
src/routes/utils/wrapWithProtection.js
Normal file
44
src/routes/utils/wrapWithProtection.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// src/routes/utils/wrapWithProtection.js
|
||||||
|
// 路由保护包装工具函数
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { PROTECTION_WRAPPER_MAP } from '../constants';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据保护模式包装组件
|
||||||
|
*
|
||||||
|
* 根据路由配置的保护模式,使用对应的保护组件包装目标组件。
|
||||||
|
* 如果没有对应的保护组件(如 PUBLIC 模式),则直接返回原组件。
|
||||||
|
*
|
||||||
|
* @param {React.ComponentType} Component - 要包装的组件
|
||||||
|
* @param {string} protection - 保护模式
|
||||||
|
* - 'modal': 使用 ProtectedRoute (弹窗登录)
|
||||||
|
* - 'redirect': 使用 ProtectedRouteRedirect (跳转登录)
|
||||||
|
* - 'public': 无保护,直接渲染
|
||||||
|
*
|
||||||
|
* @returns {React.ReactElement} 包装后的组件元素
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // PUBLIC 模式 - 无保护
|
||||||
|
* wrapWithProtection(HomePage, 'public')
|
||||||
|
* // 返回: <HomePage />
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // MODAL 模式 - 弹窗登录
|
||||||
|
* wrapWithProtection(Community, 'modal')
|
||||||
|
* // 返回: <ProtectedRoute><Community /></ProtectedRoute>
|
||||||
|
*/
|
||||||
|
export function wrapWithProtection(Component, protection) {
|
||||||
|
const WrapperComponent = PROTECTION_WRAPPER_MAP[protection];
|
||||||
|
|
||||||
|
// 如果没有对应的保护组件(PUBLIC 模式),直接返回
|
||||||
|
if (!WrapperComponent) {
|
||||||
|
return <Component />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WrapperComponent>
|
||||||
|
<Component />
|
||||||
|
</WrapperComponent>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user