一、为什么需要好的目录结构
刚开始做Vue项目时,很多人会把所有文件都堆在src目录下。随着项目越来越大,你会发现找文件就像大海捞针,改一个功能要翻遍整个项目。好的目录结构就像整理房间,东西放对位置,找起来就方便多了。
想象一下,你接手一个别人开发的项目:
- 组件散落在各处
- API调用分散在多个文件
- 工具函数重复定义
- 样式互相覆盖
这种情况我们叫"面条式代码",维护起来特别痛苦。好的目录结构能帮我们避免这些问题。
二、基础目录结构设计
我们先来看一个基础但实用的目录结构(技术栈:Vue 3 + TypeScript):
src/
├── assets/ # 静态资源
│ ├── images/ # 图片
│ └── styles/ # 全局样式
├── components/ # 公共组件
│ ├── common/ # 全平台通用组件
│ └── business/ # 业务相关组件
├── composables/ # 组合式函数
├── router/ # 路由配置
├── stores/ # 状态管理
├── utils/ # 工具函数
│ ├── http/ # 网络请求相关
│ └── helpers/ # 辅助函数
├── views/ # 页面组件
├── App.vue # 根组件
└── main.ts # 入口文件
这个结构适合中小型项目。每个目录都有明确职责:
- assets放不会变的东西
- components放可复用的UI部件
- views是路由对应的页面
- stores集中管理状态
三、进阶目录设计方案
当项目变得复杂时,我们需要更细致的划分。这里介绍两种常见方案:
方案一:按功能模块划分
src/
├── modules/
│ ├── user/ # 用户模块
│ │ ├── components/ # 模块专用组件
│ │ ├── stores/ # 模块状态
│ │ └── utils/ # 模块工具
│ └── product/ # 商品模块
├── core/ # 核心基础设施
│ ├── http/ # 网络请求
│ └── constants/ # 常量定义
└── shared/ # 共享资源
这种结构特别适合多人协作的大型项目,每个团队负责自己的模块,不会互相干扰。
方案二:按技术职责划分
src/
├── presentation/ # 展示层
│ ├── components/
│ └── pages/
├── application/ # 应用层
│ ├── services/ # 业务服务
│ └── stores/
├── domain/ # 领域层
│ ├── entities/ # 业务实体
│ └── repositories # 数据访问
└── infrastructure/ # 基础设施
├── http/
└── config/
这种结构借鉴了DDD(领域驱动设计)思想,适合业务逻辑复杂的系统。
四、关键目录详解
让我们深入看看几个重要目录的最佳实践:
1. components目录设计
好的组件分类能大幅提高开发效率。建议这样划分:
components/
├── ui/ # 基础UI组件
│ ├── buttons/
│ ├── inputs/
│ └── dialogs/
├── layout/ # 布局组件
│ ├── Header.vue
│ └── Sidebar.vue
└── business/ # 业务组件
├── ProductCard.vue
└── UserAvatar.vue
每个组件应该自包含:
<!-- components/ui/buttons/PrimaryButton.vue -->
<template>
<button
class="primary-btn"
:disabled="disabled"
@click="$emit('click')"
>
<slot></slot>
</button>
</template>
<script setup lang="ts">
defineProps({
disabled: {
type: Boolean,
default: false
}
})
defineEmits(['click'])
</script>
<style scoped>
.primary-btn {
/* 基础样式 */
}
</style>
2. API请求管理
推荐集中管理API请求:
src/
└── api/
├── modules/ # 按模块划分API
│ ├── user.ts
│ └── product.ts
├── http.ts # 请求实例配置
└── types.ts # 公共类型定义
示例API模块:
// api/modules/user.ts
import http from '../http'
import type { UserProfile, LoginParams } from '../types'
export const userApi = {
// 获取用户资料
getProfile: (userId: string) => {
return http.get<UserProfile>(`/users/${userId}`)
},
// 用户登录
login: (params: LoginParams) => {
return http.post('/auth/login', params)
}
}
五、实际场景分析
场景一:电商后台系统
对于电商系统,我们可能需要:
src/
├── modules/
│ ├── product/ # 商品管理
│ ├── order/ # 订单管理
│ ├── member/ # 会员管理
│ └── marketing/ # 营销活动
├── shared/
│ ├── components/ # 通用业务组件
│ │ ├── DataTable.vue # 数据表格
│ │ └── SearchBar.vue # 搜索栏
│ └── hooks/ # 通用逻辑
└── core/
├── permission.ts # 权限控制
└── router.ts # 路由守卫
场景二:SaaS平台
SaaS平台通常需要:
src/
├── apps/ # 子应用
│ ├── crm/ # CRM应用
│ └── erp/ # ERP应用
├── framework/ # 平台框架
│ ├── layout/ # 布局系统
│ └── plugin/ # 插件系统
└── services/ # 微服务
├── auth/ # 认证服务
└── notification # 通知服务
六、注意事项
命名一致性:整个项目保持相同的命名风格,比如全部用短横线命名法(kebab-case)
避免过度设计:小型项目不需要复杂的分层,简单直接更好
静态资源管理:
- 小图标建议用雪碧图或iconfont
- 大图片考虑CDN托管
环境变量处理:
# .env.development VITE_API_BASE=/api VITE_DEBUG=true类型安全:TypeScript类型定义要集中管理:
// types/user.d.ts interface UserProfile { id: string name: string avatar?: string }
七、技术选型考量
Vuex vs Pinia
对于新项目,建议使用Pinia:
// stores/user.ts
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
token: '',
profile: null as UserProfile | null
}),
actions: {
async login(credentials: LoginParams) {
const res = await userApi.login(credentials)
this.token = res.token
}
}
})
样式方案选择
- 简单项目:Scoped CSS
- 复杂项目:CSS Modules或TailwindCSS
八、项目演进策略
随着项目增长,目录结构也需要调整:
- 第一阶段(0-10个页面):基础结构
- 第二阶段(10-50个页面):按模块划分
- 第三阶段(50+页面):微前端或monorepo
九、总结建议
好的目录结构应该:
- 让新人能快速找到代码
- 避免重复代码
- 方便功能扩展
- 支持团队协作
记住:没有完美的方案,只有适合的方案。根据团队习惯和项目特点选择最合适的结构,并在开发过程中不断优化。
评论