当项目从一个简单的页面迭代到几十个模块、上百个页面,由十人以上的团队协作开发时,架构设计直接决定了项目的可维护性、可扩展性和开发效率。本节将拆解大型 React 项目架构设计的核心要点,提供可直接落地的实践方案。
27.3.1 大型项目的核心挑战
- 代码量膨胀:组件、状态、逻辑散落各处,耦合严重,牵一发动全身。
- 多人协作冲突:不同开发者修改同一文件,命名冲突、风格不一致。
- 业务域交错:订单模块、用户模块、权限模块各自独立,但经常需要共享数据或组件。
- 性能与加载效率:首屏加载慢、代码冗余、重复渲染。
- 可测试性与质量保障:复杂逻辑难以编写单元测试,回归测试成本高。
应对这些挑战,需要一套分层清晰、边界明确的架构体系。
27.3.2 模块分层架构
借鉴后端分层思想,前端也可以按“关注点分离”的原则进行分层,常见为三层或四层架构。
推荐分层模型:
展示层(Presentation) → 领域层(Domain) → 基础设施层(Infrastructure)
- 展示层:纯 UI 组件,只负责渲染和交互,不包含业务逻辑。例如通用 Button、Modal,或特定业务的 UserCard、OrderItem。
- 领域层:业务逻辑封装为自定义 Hooks(如
useOrderDetail)、领域模型函数(如calcDiscount),以及领域状态管理(如orderStore)。领域层决定“能做什么”和“数据怎么流转”。 - 基础设施层:提供通用技术能力,如 HTTP 请求封装、日期格式化、本地存储、日志上报、第三方 SDK 初始化等。该层与业务无关,可在不同项目中复用。
目录结构示例:
src/
├── infrastructure/ # 基础设施层
│ ├── http/ # Axios 封装、拦截器
│ ├── storage/ # localStorage/sessionStorage 封装
│ ├── logger/ # 日志收集
│ └── utils/ # 纯工具函数(lodash 等)
├── domain/ # 领域层(按业务域拆分)
│ ├── order/
│ │ ├── hooks/ # useOrderList, useOrderDetail
│ │ ├── services/ # 领域服务:orderApi, orderReducer
│ │ └── types/ # 领域模型类型
│ └── user/
├── presentation/ # 展示层
│ ├── components/ # 通用 UI 组件
│ │ ├── Button/
│ │ ├── Table/
│ │ └── Modal/
│ ├── features/ # 业务功能组件(由领域组件 + 通用组件拼装)
│ │ ├── OrderList/
│ │ └── UserProfile/
│ └── layouts/ # 布局组件(Header、Sidebar、MainContent)
├── routes/ # 路由配置
├── store/ # 全局状态(跨业务域共享)
└── App.tsx
这种分层的好处是:依赖方向自上而下,展示层可以依赖领域层和基础设施层,但基础设施层不能反向依赖业务。当业务变动时,只需修改领域层,展示层保持稳定。
27.3.3 业务域拆分
大型应用往往包含多个独立的业务线,如电商平台的“商品”“订单”“用户”“营销”等。我们将每个业务域作为一个独立的功能模块来组织,模块之间通过明确的接口通信,避免混乱耦合。
拆分原则:
- 高内聚:一个模块内部的所有代码(组件、Hooks、API、类型)共同完成该领域的功能。
- 低耦合:模块间的依赖仅限于稳定的接口(如共享的组件库、公共状态、特定服务函数),而不是直接引用对方模块的内部文件。
- 可独立交付:理想情况下,一个业务域可以独立开发、测试和部署(如果配合微前端则更彻底)。
模块内部结构:
domain/order/
├── components/ # 仅本模块使用的展示组件
├── hooks/ # 业务逻辑 Hooks
├── services/ # 数据获取与处理
├── types/ # 订单相关的 TypeScript 接口
└── index.ts # 对外暴露的公共接口(如少数共享组件或 Hooks)
模块间通信方式:
- 通过 URL 参数/路由传递:例如从订单列表点击进入订单详情,通过路由参数传递
orderId。 - 通过全局状态:如用户登录信息、全局主题等,由
store/user模块管理,其他模块读取。 - 通过事件总线(不推荐)或回调函数:少量跨模块交互可使用 Context + 回调,避免滥用全局状态。
- 共享领域服务:例如
userService.getCurrentUser(),可被多个领域模块调用,但不直接耦合 UI。
27.3.4 组件库设计与公共能力沉淀
大型项目必然沉淀出大量可复用组件和基础能力,将它们抽象为“组件库”或“通用工具包”可以大幅提升效率。
分层设计组件:
- 基础组件(Primitives):Button、Input、Modal、Tooltip 等,与业务完全无关,追求通用性和易用性。可参考 Ant Design 的设计规范自研,或直接选型成熟 UI 库二次封装。
- 业务组件(Widgets):结合基础组件与特定领域产生的复合组件,例如
UserSelector(带搜索的选人弹窗)、OrderStatusTag、MoneyDisplay。它们对业务有语意,但跨模块复用。 - 页面模板(Templates):典型的列表页、详情页、表单页模板,固化交互模式。
组件开发规范:
- 每个组件独立目录,包含
index.tsx、types.ts、style.module.css(或 styled-components)、tests。 - 使用 TypeScript 定义清晰的 Props 接口,并导出类型供使用者引用。
- 组件应默认支持
ref转发(如有必要),通过React.forwardRef暴露底层 DOM。 - 使用 Storybook 构建组件文档和演示,方便跨团队共享和调试。
公共能力沉淀清单:
- 自定义 Hooks 库:如
useRequest(数据请求)、usePagination、usePermission、useForm(集成 React Hook Form)、useDebounce等,统一业务逻辑的调用方式。 - 工具函数集:日期格式化、数字格式化、权限判断、埋点上报等,集中管理避免到处复制。
- 请求层抽象:统一错误处理、Token 刷新、Loading 状态、接口类型,让开发者专注
api.order.list()而不关心 HTTP 细节。 - 状态管理模块:将全局状态按领域划分为 store slices(Zustand/Redux 均可),提供清晰的 action 和 selector。
27.3.5 路由与权限体系
大型项目的路由往往复杂且动态,需与权限深度整合。
路由模块设计:
// routes/index.tsx
const dashboardRoutes = { ... };
const orderRoutes = { ... };
const settingsRoutes = { ... };
// 合并并根据权限过滤
const protectedRoutes = [dashboardRoutes, orderRoutes, settingsRoutes];
权限控制方案:
- 路由权限:在路由配置中定义
permission字段,渲染前检查用户是否拥有该权限。可使用高阶组件或自定义usePermissionHook 包裹。 - 组件级权限:按钮、菜单项等细微粒度的权限控制,通过
if (hasPermission('order:delete'))包裹。 - 菜单权限:后台管理系统的菜单通常与路由一一对应,根据用户权限动态生成可见菜单。
示例:
// components/AuthRoute.tsx
function AuthRoute({ children, permission }) {
const hasAccess = usePermission(permission);
if (!hasAccess) return <Navigate to="/403" replace />;
return children;
}
27.3.6 状态管理策略
除了局部组件状态,大型应用还需要管理跨模块共享的状态,需要分层使用:
- UI 状态:如弹窗的显隐、表单的临时数据,使用组件内
useState或useReducer。 - 服务器缓存状态:使用 TanStack Query 或 SWR,将获取的数据自动缓存、同步,避免手动管理 loading/error。
- 全局业务状态:如用户信息、全局配置,使用 Zustand 或 Redux Toolkit,按领域拆分 slice。
- URL 状态:如搜索条件、分页信息,始终存储在 URL query 上,保证分享和刷新后可恢复。
状态选择原则是最小暴露原则:一个状态的作用域越小越好,能用组件内部状态就不提升,能用 Context 就不上全局 store。
27.3.7 构建与部署优化
大型项目构建时间和打包体积是必须关注的指标。
构建优化:
- 使用 Vite 或 Turbopack 等高速构建工具,利用 ESM 按需编译。
- 代码分割:路由级懒加载(
React.lazy),合理拆分 vendor chunks。 - 依赖外置:将 React、ReactDOM 等大型库通过 CDN 引入(可选,需权衡缓存与加载)。
- 图片与字体优化:使用压缩、WebP 格式、按需加载。
部署策略:
- 多环境隔离:dev、test、staging、production 环境变量区分。
- CI/CD 流水线集成:代码质量检查(ESLint + TypeScript + 单测) → 构建 → 部署到 CDN 或静态服务器。
- 微前端部署:如果采用 Module Federation 或 qiankun,各子应用可独立部署和灰度发布。
27.3.8 工程规范与质量保障
大型项目必须建立统一的规范和自动化校验,否则质量无从谈起。
- 代码规范:ESLint + Prettier 统一代码风格,配置文件纳入版本管理。
- Git 提交规范:使用 Commitlint + Husky 强制 commit message 格式,可自动生成 changelog。
- 类型安全:全量开启 TypeScript strict 模式,禁止滥用
any。 - 测试策略:
- 单元测试:核心工具函数、复杂 Hooks、关键业务逻辑覆盖率 > 80%。
- 组件测试:使用 React Testing Library 测试用户交互行为。
- E2E 测试:Cypress/Playwright 保障核心业务流程(如登录、下单)可用。
- 监控与日志:接入前端监控系统(Sentry 等),收集错误和性能数据,及时发现问题。
27.3.9 一个真实项目的落地目录结构
以下是一个典型的电商后台管理项目目录结构,可作为参考起点:
project/
├── public/
├── src/
│ ├── infrastructure/ # 基础设施
│ │ ├── http/ # axios 实例、拦截器
│ │ ├── storage/ # 缓存工具
│ │ ├── utils/ # 通用工具(日期、数字格式化……)
│ │ └── logger/ # 日志与埋点
│ ├── domain/ # 业务领域
│ │ ├── auth/ # 登录、权限
│ │ │ ├── hooks/ # useLogin, usePermission
│ │ │ ├── services/ # authApi.ts
│ │ │ └── types/
│ │ ├── order/
│ │ └── product/
│ ├── presentation/ # 展示层
│ │ ├── base/ # 基础 UI 组件(Button, Modal……)
│ │ ├── business/ # 业务组件(OrderCard, ProductSelector……)
│ │ └── layout/ # 布局(AdminLayout, AuthLayout)
│ ├── routes/ # 路由配置、权限守卫
│ │ ├── index.tsx
│ │ └── routeConfig.ts
│ ├── store/ # 全局状态(Zustand/Redux)
│ │ ├── userStore.ts
│ │ └── appStore.ts
│ ├── hooks/ # 全局共享 Hooks(useDebounce, usePagination...)
│ ├── types/ # 全局类型定义
│ ├── App.tsx
│ └── main.tsx
├── .eslintrc.cjs
├── .prettierrc
├── tsconfig.json
├── vite.config.ts
└── package.json
这个结构完美地体现了模块分层、业务域拆分和公共能力沉淀。实际项目中,可根据团队规模和业务复杂度灵活调整,但核心原则不变:边界清晰、依赖单向、高内聚低耦合。
通过上述架构设计,一个大型 React 项目能够在保持高开发效率的同时,具备优秀的可维护性与可扩展性。架构不是固定的教条,而应随着项目演进持续优化,但分层的思想和关注点分离的原则是长期有效的基石。