出处:掘金
原作者:金泽宸
模块化 + 领域分层 + 插件化目录,是大型项目的“骨架系统”
项目越大,目录越乱,是所有前端架构师绕不过去的坑。我们常常见到这样的代码结构:
pages/
├── page1.vue
├── page2.vue
├── page3.vue
components/
├── common/
├── header.vue
├── dialog.vue
utils/
api/
很快你就发现:
今天这一篇,我们通过实战项目演示:如何设计清晰、高可维护、方便多人协作的大型项目结构
| 模式 | 特点 | 适合场景 |
|---|---|---|
| 按功能划分(Flat) | 易于上手、轻量 | 小项目、单人开发 |
| 按页面划分(Views) | 页面结构清晰 | 中型项目、界面主导 |
| 按领域划分(Domain Oriented)✅ | 高内聚、低耦合 | 大型项目、多人协作、可平台化发展 |
src/
├── modules/ # 各个业务模块(重点)
│ ├── user/
│ │ ├── views/ # 页面(profile、login等)
│ │ ├── components/ # 业务组件
│ │ ├── api.ts # 模块内接口
│ │ ├── store.ts # 模块状态
│ │ └── index.ts # 模块入口
│ ├── order/
│ └── dashboard/
├── shared/ # 通用能力封装
│ ├── components/ # 通用组件(弹窗、表单)
│ ├── hooks/ # 通用 hooks(useDialog、useTable)
│ ├── utils/ # 工具函数
│ └── constants/ # 枚举/状态码
├── router/ # 动态路由加载逻辑
├── store/ # 全局状态,如用户/主题
├── assets/ # 静态资源
├── services/ # 和后端联动的接口封装
├── App.vue
└── main.ts
modules/user/
├── views/
│ ├── Login.vue
│ ├── Profile.vue
├── components/
│ ├── UserAvatar.vue
│ ├── UserInfoCard.vue
├── api.ts
├── store.ts
└── index.ts
模块 API:
// modules/user/api.ts
import request from '@/shared/utils/request'
export const getUserInfo = () => request.get('/user/info')
export const login = (data) => request.post('/user/login', data)
好处:
步骤1:在模块中暴露路由配置:
// modules/user/index.ts
export default {
routes: [
{
path: '/user/login',
component: () => import('./views/Login.vue'),
},
{
path: '/user/profile',
component: () => import('./views/Profile.vue'),
},
],
}
步骤2:在全局 router 中自动加载所有模块路由:
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
const modules = import.meta.glob('../modules/**/index.ts', { eager: true })
const routes = Object.values(modules)
.map((m: any) => m.default.routes)
.flat()
export const router = createRouter({
history: createWebHistory(),
routes,
})
优点:
| 目录位置 | 用途 | 示例组件 |
|---|---|---|
shared/components/ |
通用组件 | Dialog、Pagination、DatePicker |
modules/user/components/ |
业务组件 | UserAvatar、UserStatsCard |
pages/组件目录 |
页面私有组件 | TableColumnRenderer.vue |
建议:
<!-- 通用组件 Dialog -->
<MyDialog :title="title" @confirm="handleConfirm">
<template #default>
<p>{{ description }}</p>
</template>
</MyDialog>
<!-- 业务中使用 -->
<MyDialog title="删除用户" @confirm="deleteUser">
<template #default>
<p>是否确认删除该用户?</p>
</template>
</MyDialog>