前端工程结构设计指南:如何让模块解耦、易维护、可拓展
简述痛点:「当项目代码越来越大,一个修改往往影响多个模块,导致代码耦合、修改麻烦。」 点出目标:「本文分享一套结构设计和代码组织原则,适用于 React/Vue 项目,助力构建可维护、可拓展的前端工程。」
举例说明:
一个模块修改,影响到其他模块。
接口参数相同但结构不同,代码堆砌。
通用组件和专用组件混淆。
你是否有过因为修改一个模块,导致其他页面崩掉的经历?
设计原则:
通用和专用分离。
接口参数化设计。
一个组件,多态展示。
可共用部分放 shared。
模块隔离,修改互不影响。
以 React 为例,展示结构:
src/
├─ app/ # 整个应用的入口和路由配置
│ └─ App.tsx
├─ features/
│ └─ user/ # 用户模块
│ ├─ pages/
│ │ └─ UserList.tsx
│ │ └─ UserDetail.tsx
│ └─ state/
│ └─ userAtoms.ts
│ └─ api.ts # 当前模块的接口封装
│ └─ components/
│ └─ UserCard.tsx
├─ features/
│ └─ order/ # 订单模块
│ └─ pages/
│ └─ state/
│ └─ api.ts
├─ shared/ # 通用共享部分
│ └─ components/
│ └─ hooks/
│ └─ utils/
├─ main.tsx
├─ vite.config.ts
├─ index.html
以 Vue 为例,展示结构
src/
├─ shared/ # 通用共享部分
│ └─ components/ # 通用组件 (Button、Input、Table…)
│ └─ Button.vue
│ └─ Table.vue
│ └─ api/
│ └─ httpClient.ts # 通用请求封装
├─ features/
│ └─ user/ # 用户模块
│ └─ components/ # 用户模块专用组件
│ └─ UserCard.vue
│ └─ api.ts # 用户接口,参数化管理
│ └─ pages/
│ └─ UserList.vue
│ └─ UserDetail.vue
├─ app/
│ └─ router.ts # 路由配置
├─ stores/
│ └─ userStore.ts # 用户模块 Pinia Store
通用组件
放在 shared/components
只和显示有关,例如:
Button.vue
InputField.vue
Table.vue
模块专用组件
放在 features/{module}/components
只在这个模块使用,例如:
UserCard.vue (显示用户头像和名字)
接口参数不同但结构相同
在 features/{module}/api.ts 里参数化
完全共用接口
放在 shared/api,如:
shared/api/auth.ts
shared/api/common.ts
一个组件因为参数不同而显示不同
用 Props 控制行为
示例代码片段:
通用组件
接口参数化
用 props 控制行为
简明扼要,不贴过大代码片段。
优点:
修改不影响其他模块。
可快速迭代。
可为新成员提供清晰结构。
缺点:
对小型项目结构略重。
学习和维护结构需要共识。
重申结构设计意义。
呼吁读者在实际项目中尝试。
提供参考方向和建议。
可附参考链接:
微前端概念。
比如说:
Module Federation
qiankun
single-spa
模块化设计。
可维护性设计文章。
修改 user 模块只需要修改 features/user,不会碰到其他模块
每个模块都有自己的:
pages/ —— 路由页面
state/ —— Recoil atoms/selectors
api.ts —— 接口请求
components/ —— 模块专属组件
通用部分放 shared/
路由集中管理在 app/App.tsx
增加新模块只需要
在 features/ 创建新模块结构。
在 app/App.tsx 增加新路由。
完全不会碰老模块代码。
如果是跨模块共用,放 shared/components。
如果是只在某模块共用,放 features/[module]/components。
如果是因为参数不同显示不同内容:保留一个组件,通过 props 控制行为。
在总结里强调:
模块化是让前端大中型项目可维护、可拓展的核心。
明确结构和职责分配,避免耦合。
找到共性和个性之间的平衡。
结构设计是为后续迭代和维护服务。