在构建小型应用时,几乎任何结构都能跑得起来。但一旦项目开始增长——组件变多、业务逻辑复杂、接入 API、引入状态管理——如果文件夹结构一开始没想清楚,混乱将如影随形。
本文将分享一套在现代化 React 项目中经过实践验证、具备高度可扩展性的文件夹结构方案。无论你是启动个人项目,还是规划一个需要长期维护的企业级应用,这套结构都能帮助你保持代码组织清晰、职责边界明确,让团队协作和后续扩展更加顺畅。
为什么文件夹结构至关重要?
在深入细节之前,或许你会问:为什么要在意文件夹怎么放?
因为一个深思熟虑的结构远不止是“整理癖”,它能带来切实的工程价值:
- 提升可维护性:让新成员(或三个月后的你自己)能快速理解项目脉络,降低上手成本。
- 明确职责边界:清晰地区分 UI 组件、业务逻辑与数据获取层,避免代码纠缠。
- 减少缺陷与重复:逻辑集中管理,自然降低了重复代码和因散落各处导致的 Bug。
- 拥抱变化:为功能扩展和模块化开发提供坚实基础,使项目能优雅地应对需求增长。
核心结构设计:清晰、可扩展、开发者友好
以下是围绕“清晰度”核心构建的一个推荐项目结构示例,适用于基于 Vite + TypeScript 的 React 项目。
.
├── eslint.config.js
├── index.html
├── package.json
├── public
│ └── vite.svg
├── README.md
├── src
│ ├── App.css
│ ├── App.tsx
│ ├── assets # 静态资源:图片、字体、图标等
│ │ └── react.svg
│ ├── components # 可复用的纯 UI 组件
│ │ └── Button
│ │ ├── Button.module.css
│ │ ├── Button.test.ts
│ │ └── index.tsx
│ ├── features # 业务功能模块
│ │ └── auth
│ │ ├── AuthPage.tsx
│ │ ├── components
│ │ ├── hooks
│ │ └── services
│ ├── hooks # 自定义 React Hooks
│ ├── layouts # 布局组件(如 DashboardLayout, AuthLayout)
│ ├── pages # 页面组件,与路由结构对应
│ ├── routes # 路由配置(如 React Router 配置)
│ ├── services # API 接口调用层
│ ├── store # 全局状态管理
│ ├── types # TypeScript 类型定义
│ ├── utils # 工具函数、常量、辅助方法
│ ├── config # 应用配置、主题、环境变量等
│ ├── index.css
│ └── main.tsx
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
1. Components 与 Features 的职责分离
这是保持项目模块化的关键。
/components:这里存放纯 UI 组件。它们应该是无状态的、高度可复用的,并且不关心具体的业务逻辑。例如:Button、Input、Modal、Card。它们可以在整个应用甚至不同项目间使用。
/features:这里组织具体的业务功能。每个功能领域(如 auth认证、chat聊天、profile用户资料)都拥有自己的文件夹。其内部通常可以进一步包含:
components/ - 该功能专属的、带有业务逻辑的组件
services/ - 功能相关的 API 调用
hooks/ - 功能相关的自定义 Hooks
pages/ - 功能相关的页面
- 甚至可以包含自己的
store/ - 功能级别的状态管理
这种方式实现了“关注点分离”和“逻辑就近收敛”,有效防止了全局状态膨胀和文件散落各处导致的混乱。
2. Pages 与 Routes 保持映射
让 pages/ 目录的结构与你的应用路由保持一致。每个页面组件对应一个路由,这使得通过 URL 路径来定位页面文件变得非常直观。页面组件可以自由地引入 features/ 中的模块和 layouts/ 中的布局。
3. 集中化的 Services 层
一个重要的原则:API 调用不应直接散落在 UI 组件中。
使用 services/ 文件夹来统一管理所有与后端通信的逻辑。例如,创建一个 auth.ts 文件:
export const login = (credentials: LoginCredentials) =>
axios.post('/api/login', credentials);
UI 组件只应关心如何展示数据和交互,而不应处理 HTTP 请求的细节。这种集中管理的方式,使得当后端 API 端点或数据结构发生变化时,你只需修改 services/ 目录下的对应文件,维护成本大大降低。
4. 谨慎使用全局 Store
不要为了用而用全局状态管理。优先考虑将状态管理在 features/ 模块内部,使用 React 组件自身的 useState、useReducer 或功能模块内的 Context。只有当某些状态确实需要在多个相距甚远、且无直接关联的功能模块间共享时,才应考虑引入 Redux、Zustand 或创建全局的 Context。这避免了不必要的状态复杂度和性能开销。
5. 利用 Hooks 实现逻辑复用
自定义 Hook 是 React 项目实现逻辑复用的利器。无论是 useAuth、useLocalStorage、useDebounce 还是 useForm,将它们统一放置在 /hooks 目录下。这使得你可以在任何组件中无痛地复用复杂逻辑,尤其适用于:
- 数据获取(API fetching)
- 表单状态与验证
- 浏览器 API 交互(如计时器、事件监听)
- 动画或副作用的封装
总结
没有一套文件夹结构是放之四海而皆准的银弹,但本文分享的布局已在众多个人项目、团队协作及快速原型开发中被反复验证。其核心思想在于:通过约定大于配置的方式,强制形成清晰的模块边界和职责分离。
记住三个关键:保持模块化、保持整洁、保持清晰。随着项目演进,你可以根据团队习惯和业务特点灵活调整,但万变不离其宗——良好的结构是长期维护性的基石。如果你对更多前端工程化实践感兴趣,欢迎在云栈社区的前端框架与工程化板块交流探讨。