大型 React 项目的文件结构

🏗️ 推荐的目录结构

复制代码
src/
├── app/                    # 应用核心配置
│   ├── routes/            # 路由配置
│   ├── store/             # Redux/Zustand 状态管理
│   └── providers/         # 全局 Providers 组合
├── features/              # 功能模块(核心业务)
│   ├── auth/
│   │   ├── components/    # 模块私有组件
│   │   ├── hooks/         # 模块私有 hooks
│   │   ├── services/      # API 调用
│   │   ├── types/         # TypeScript 类型
│   │   ├── utils/         # 工具函数
│   │   └── index.ts       # 公共导出
│   ├── dashboard/
│   └── user/
├── components/            # 共享通用组件
│   ├── ui/               # 基础 UI 组件(Button, Input)
│   ├── layout/           # 布局组件(Header, Sidebar)
│   └── shared/           # 业务共享组件(UserAvatar, DataTable)
├── hooks/                 # 全局共享 hooks
├── lib/                   # 第三方库配置
│   ├── api/              # HTTP 客户端配置
│   └── react-query.ts    # React Query 配置
├── utils/                 # 纯函数工具
├── types/                 # 全局类型定义
├── constants/             # 常量定义
├── styles/                # 全局样式
└── assets/                # 静态资源

📁 详细说明与示例

1. features/ - 功能模块(核心)

这是最重要的目录,按业务功能组织代码:

typescript 复制代码
// features/auth/types/index.ts
export interface User {
  id: string;
  email: string;
  name: string;
  role: 'admin' | 'user';
}

// features/auth/services/authService.ts
import { api } from '@/lib/api';

export const authService = {
  login: (credentials: { email: string; password: string }) =>
    api.post<User>('/auth/login', credentials),
  
  logout: () => api.post('/auth/logout'),
  
  getCurrentUser: () => api.get<User>('/auth/me'),
};

// features/auth/hooks/useAuth.ts
import { useQuery, useMutation } from '@tanstack/react-query';
import { authService } from '../services';

export function useAuth() {
  const { data: user, isLoading } = useQuery({
    queryKey: ['auth', 'user'],
    queryFn: authService.getCurrentUser,
  });

  const loginMutation = useMutation({
    mutationFn: authService.login,
    onSuccess: (data) => {
      // 处理登录成功
    },
  });

  return {
    user,
    isLoading,
    login: loginMutation.mutate,
    isLoggingIn: loginMutation.isPending,
  };
}

// features/auth/components/LoginForm.tsx
import { useAuth } from '../hooks/useAuth';

export function LoginForm() {
  const { login, isLoggingIn } = useAuth();
  
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    const formData = new FormData(e.target as HTMLFormElement);
    login({
      email: formData.get('email') as string,
      password: formData.get('password') as string,
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="email" type="email" required />
      <input name="password" type="password" required />
      <button type="submit" disabled={isLoggingIn}>
        {isLoggingIn ? '登录中...' : '登录'}
      </button>
    </form>
  );
}

// features/auth/index.ts - 统一导出
export { LoginForm } from './components/LoginForm';
export { useAuth } from './hooks/useAuth';
export type { User } from './types';

2. components/ - 共享组件

tsx 复制代码
// components/ui/Button/Button.tsx
import { cva, type VariantProps } from 'class-variance-authority';

const buttonVariants = cva(
  'rounded-md font-medium transition-colors',
  {
    variants: {
      variant: {
        primary: 'bg-blue-600 text-white hover:bg-blue-700',
        secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
        danger: 'bg-red-600 text-white hover:bg-red-700',
      },
      size: {
        sm: 'px-3 py-1.5 text-sm',
        md: 'px-4 py-2 text-base',
        lg: 'px-6 py-3 text-lg',
      },
    },
    defaultVariants: {
      variant: 'primary',
      size: 'md',
    },
  }
);

interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  loading?: boolean;
}

export const Button: React.FC<ButtonProps> = ({
  variant,
  size,
  loading,
  children,
  disabled,
  ...props
}) => {
  return (
    <button
      className={buttonVariants({ variant, size })}
      disabled={disabled || loading}
      {...props}
    >
      {loading && <Spinner className="mr-2" />}
      {children}
    </button>
  );
};

3. app/store/ - 状态管理

typescript 复制代码
// app/store/store.ts (Redux Toolkit 示例)
import { configureStore } from '@reduxjs/toolkit';
import { authReducer } from '@/features/auth/store/authSlice';
import { uiReducer } from './slices/uiSlice';

export const store = configureStore({
  reducer: {
    auth: authReducer,
    ui: uiReducer,
  },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

// app/store/hooks.ts - 类型化的 hooks
import { useDispatch, useSelector } from 'react-redux';
import type { RootState, AppDispatch } from './store';

export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
export const useAppSelector = useSelector.withTypes<RootState>();

4. lib/api/ - HTTP 客户端配置

typescript 复制代码
// lib/api/client.ts
import axios from 'axios';
import { authService } from '@/features/auth/services';

const apiClient = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_URL,
  timeout: 30000,
});

// 请求拦截器
apiClient.interceptors.request.use((config) => {
  const token = localStorage.getItem('accessToken');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// 响应拦截器
apiClient.interceptors.response.use(
  (response) => response.data,
  async (error) => {
    if (error.response?.status === 401) {
      // 刷新 token
      await authService.refreshToken();
      return apiClient(error.config);
    }
    return Promise.reject(error);
  }
);

export { apiClient };

5. app/providers/ - 全局 Providers

tsx 复制代码
// app/providers/AppProviders.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Provider as ReduxProvider } from 'react-redux';
import { store } from '../store';
import { ThemeProvider } from './ThemeProvider';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5, // 5分钟
      retry: 1,
    },
  },
});

interface AppProvidersProps {
  children: React.ReactNode;
}

export function AppProviders({ children }: AppProvidersProps) {
  return (
    <ReduxProvider store={store}>
      <QueryClientProvider client={queryClient}>
        <ThemeProvider>
          {children}
        </ThemeProvider>
      </QueryClientProvider>
    </ReduxProvider>
  );
}

🎯 大型项目的最佳实践

1. 模块独立性原则

每个 feature 应该是自包含的,可以独立开发和测试:

复制代码
features/
├── product/
│   ├── components/     # 只服务于 product 模块
│   ├── hooks/          # 只服务于 product 模块
│   ├── services/       # product 相关的 API
│   ├── types/          # product 相关的类型
│   └── __tests__/      # product 模块的测试

2. 导入路径规范

使用路径别名避免深层相对路径:

json 复制代码
// tsconfig.json
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"],
      "@features/*": ["./src/features/*"],
      "@components/*": ["./src/components/*"],
      "@utils/*": ["./src/utils/*"],
      "@hooks/*": ["./src/hooks/*"]
    }
  }
}

使用示例:

typescript 复制代码
// ❌ 避免
import Button from '../../../components/ui/Button';

// ✅ 推荐
import { Button } from '@/components/ui/Button';
import { useAuth } from '@features/auth';

3. 文件名命名约定

typescript 复制代码
// 组件文件:PascalCase
LoginForm.tsx
UserProfile.tsx

// Hook 文件:camelCase with 'use' prefix
useAuth.ts
useDebounce.ts

// 工具函数:camelCase
formatDate.ts
validateEmail.ts

// 类型文件:camelCase or PascalCase
user.types.ts
api.types.ts

// 常量文件:UPPER_CASE
API_CONSTANTS.ts

4. 测试文件组织

typescript 复制代码
// 测试文件紧邻源文件
features/
├── auth/
│   ├── components/
│   │   ├── LoginForm.tsx
│   │   └── LoginForm.test.tsx    # 单元测试
│   ├── hooks/
│   │   ├── useAuth.ts
│   │   └── useAuth.test.ts
│   └── __tests__/                # 集成测试
│       └── auth.integration.test.ts

📊 不同规模项目的结构对比

项目规模 目录结构 何时采用
小型 (1-2周) components/, pages/, hooks/, utils/ 原型验证、内部工具
中型 (1-3月) 添加 features/, services/, types/ SaaS 产品、B端应用
大型 (3月+) 完整结构 + 微前端 企业级应用、多团队协作
复制代码
相关推荐
AI_零食1 小时前
健身室器材管理系统鸿蒙PC Electron框架编写深度解析
前端·javascript·学习·华为·electron·前端框架·鸿蒙
ZC跨境爬虫2 小时前
跟着 MDN 学 JavaScript day_2:JavaScript 初体验
开发语言·前端·javascript·学习·ecmascript
假如让我当三天老蒯2 小时前
useCallback 详细解释(从原理到用法)(自学用)
前端·react.js
a1117762 小时前
粒子化系统(3D-Particles)THreeJS react
前端·html·jetson
码农君莫笑2 小时前
深入理解 CSS Grid 布局:从入门到实战
前端·css
Vu4612 小时前
nextjs的图片和文字优化
react.js
yingyima3 小时前
Azure Functions 定时触发器配置:Cron vs. TimerTrigger,谁主沉浮?
前端
TeamDev3 小时前
JxBrowser 9.1.1 版本发布啦!
java·前端·chromium·混合应用·jxbrowser·嵌入式浏览器·浏览器控件