项目结构 #
一、项目结构概述 #
1.1 为什么需要良好的项目结构 #
| 优势 | 说明 |
|---|---|
| 可维护性 | 代码易于查找和修改 |
| 可扩展性 | 新功能易于添加 |
| 可读性 | 团队成员易于理解 |
| 复用性 | 组件和逻辑易于复用 |
1.2 基本原则 #
- 按功能模块组织
- 相关文件放在一起
- 保持结构扁平
- 命名清晰一致
二、常见项目结构 #
2.1 按类型组织 #
text
src/
├── components/ # 组件
│ ├── common/ # 通用组件
│ ├── layout/ # 布局组件
│ └── ui/ # UI组件
├── hooks/ # 自定义Hooks
├── pages/ # 页面组件
├── services/ # API服务
├── store/ # 状态管理
├── utils/ # 工具函数
├── constants/ # 常量
├── styles/ # 全局样式
└── App.jsx
2.2 按功能模块组织(推荐) #
text
src/
├── features/ # 功能模块
│ ├── auth/
│ │ ├── components/
│ │ ├── hooks/
│ │ ├── services/
│ │ ├── store/
│ │ └── index.js
│ ├── todos/
│ │ ├── components/
│ │ ├── hooks/
│ │ ├── store/
│ │ └── index.js
│ └── users/
├── components/ # 共享组件
│ ├── Button/
│ ├── Input/
│ └── Modal/
├── hooks/ # 共享Hooks
├── layouts/ # 布局组件
├── pages/ # 页面入口
├── services/ # API服务
├── store/ # 全局状态
├── utils/ # 工具函数
└── App.jsx
2.3 小型项目结构 #
text
src/
├── components/
│ ├── Header.jsx
│ ├── Footer.jsx
│ └── TodoList.jsx
├── hooks/
│ └── useTodos.js
├── App.jsx
└── main.jsx
三、组件组织 #
3.1 组件文件夹结构 #
text
components/
├── Button/
│ ├── Button.jsx # 组件实现
│ ├── Button.test.jsx # 测试文件
│ ├── Button.module.css # 样式文件
│ ├── Button.stories.jsx # Storybook
│ └── index.js # 导出文件
3.2 组件文件模板 #
javascript
// Button.jsx
import styles from './Button.module.css';
export function Button({ children, variant = 'primary', onClick }) {
return (
<button
className={`${styles.button} ${styles[variant]}`}
onClick={onClick}
>
{children}
</button>
);
}
// index.js
export { Button } from './Button';
export { default } from './Button';
3.3 组件分类 #
text
components/
├── ui/ # 基础UI组件
│ ├── Button/
│ ├── Input/
│ ├── Select/
│ └── Modal/
├── layout/ # 布局组件
│ ├── Header/
│ ├── Footer/
│ ├── Sidebar/
│ └── Layout/
└── common/ # 通用业务组件
├── UserCard/
├── ProductCard/
└── SearchBar/
四、状态管理组织 #
4.1 Context组织 #
text
contexts/
├── AuthContext.jsx
├── ThemeContext.jsx
├── CartContext.jsx
└── index.js
4.2 Redux组织 #
text
store/
├── index.js
├── rootReducer.js
└── middleware/
features/
├── auth/
│ ├── authSlice.js
│ ├── authSelectors.js
│ └── authThunks.js
└── todos/
├── todosSlice.js
├── todosSelectors.js
└── todosThunks.js
五、路由组织 #
5.1 路由配置 #
text
routes/
├── index.jsx # 路由入口
├── publicRoutes.jsx # 公开路由
├── protectedRoutes.jsx # 受保护路由
└── constants.js # 路由常量
5.2 路由示例 #
javascript
// routes/index.jsx
import { createBrowserRouter } from 'react-router-dom';
import { publicRoutes } from './publicRoutes';
import { protectedRoutes } from './protectedRoutes';
export const router = createBrowserRouter([
...publicRoutes,
...protectedRoutes
]);
// routes/publicRoutes.jsx
export const publicRoutes = [
{
path: '/',
element: <Home />
},
{
path: '/login',
element: <Login />
}
];
// routes/protectedRoutes.jsx
export const protectedRoutes = [
{
path: '/dashboard',
element: (
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
)
}
];
六、API服务组织 #
6.1 服务层结构 #
text
services/
├── api/
│ ├── client.js # Axios实例
│ ├── interceptors.js # 拦截器
│ └── endpoints.js # 端点配置
├── auth/
│ └── authService.js
├── user/
│ └── userService.js
└── index.js
6.2 服务示例 #
javascript
// services/api/client.js
import axios from 'axios';
export const apiClient = axios.create({
baseURL: import.meta.env.VITE_API_URL,
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
});
// services/auth/authService.js
import { apiClient } from '../api/client';
export const authService = {
login: (credentials) =>
apiClient.post('/auth/login', credentials),
logout: () =>
apiClient.post('/auth/logout'),
refreshToken: (token) =>
apiClient.post('/auth/refresh', { token }),
getCurrentUser: () =>
apiClient.get('/auth/me')
};
七、工具函数组织 #
7.1 工具目录 #
text
utils/
├── format/
│ ├── date.js
│ ├── number.js
│ └── string.js
├── validation/
│ ├── form.js
│ └── schema.js
├── storage/
│ ├── localStorage.js
│ └── sessionStorage.js
└── helpers/
├── array.js
└── object.js
7.2 工具示例 #
javascript
// utils/format/date.js
export const formatDate = (date, format = 'YYYY-MM-DD') => {
// 格式化日期
};
export const formatTime = (date) => {
// 格式化时间
};
// utils/validation/form.js
export const isEmail = (value) => {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
};
export const isPhone = (value) => {
return /^1[3-9]\d{9}$/.test(value);
};
八、样式组织 #
8.1 样式目录 #
text
styles/
├── variables.css # CSS变量
├── reset.css # 重置样式
├── global.css # 全局样式
├── utilities.css # 工具类
└── themes/
├── light.css
└── dark.css
8.2 CSS模块 #
css
/* components/Button/Button.module.css */
.button {
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
}
.primary {
background-color: var(--color-primary);
color: white;
}
.secondary {
background-color: var(--color-secondary);
color: white;
}
九、配置文件 #
9.1 环境变量 #
bash
# .env.development
VITE_API_URL=http://localhost:3000/api
VITE_APP_TITLE=React App (Dev)
# .env.production
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=React App
9.2 项目配置 #
javascript
// config/index.js
export const config = {
api: {
baseURL: import.meta.env.VITE_API_URL,
timeout: 10000
},
app: {
title: import.meta.env.VITE_APP_TITLE
},
features: {
darkMode: true,
i18n: true
}
};
十、最佳实践 #
10.1 命名规范 #
| 类型 | 规范 | 示例 |
|---|---|---|
| 组件 | PascalCase | UserCard.jsx |
| Hook | camelCase + use前缀 | useAuth.js |
| 工具函数 | camelCase | formatDate.js |
| 常量 | UPPER_SNAKE_CASE | API_ENDPOINTS |
| CSS模块 | camelCase | button.module.css |
10.2 导出规范 #
javascript
// 命名导出
export { Button };
export { useAuth };
// 默认导出(页面组件)
export default HomePage;
// 统一导出
export { Button } from './Button';
export { IconButton } from './IconButton';
10.3 导入顺序 #
javascript
// 1. React相关
import { useState, useEffect } from 'react';
// 2. 第三方库
import { BrowserRouter } from 'react-router-dom';
import { useSelector } from 'react-redux';
// 3. 内部模块(按层级)
import { Button } from '@/components/Button';
import { useAuth } from '@/hooks/useAuth';
import { formatDate } from '@/utils/format';
// 4. 样式
import styles from './App.module.css';
十一、总结 #
| 要点 | 说明 |
|---|---|
| 按功能组织 | 相关文件放在一起 |
| 组件分类 | UI组件、布局组件、业务组件 |
| 服务层 | 统一API调用 |
| 命名规范 | 保持一致性 |
核心原则:
- 保持结构清晰
- 相关文件就近放置
- 遵循命名规范
- 合理拆分模块
最后更新:2026-03-26