【Vue3】+【中后台前端开发】:从【按业务域划分目录结构】到【落地实操】,彻底搞懂【可维护前端代码】的最佳写法,避开目录混乱、维护困难高频坑!

同学们好,我是 Eugene(尤金),一名多年中后台前端开发工程师。
(Eugene 发音 /juːˈdʒiːn/,大家怎么顺口怎么叫就好)
很多前端开发者都会遇到一个瓶颈:
代码能跑,但不够规范;功能能实现,但维护起来特别痛苦;
一个人写没问题,一到团队协作就各种混乱、踩坑、返工。
想写出干净、优雅、可维护的专业代码,
靠的不是天赋,而是体系化的规范 + 真实实战经验。
这一系列《前端规范实战》,我会用大白话 + 真实业务场景,
不讲玄学、不堆理论,只分享能直接落地的规范、标准与避坑指南。
帮你从「会写代码」真正升级为「会写优质、可维护、团队级别的代码」。
一、开篇:这篇文章能帮你解决什么问题
工作里常见几类问题:
- 新人接手项目,不知道功能写在哪
- 页面一多,文件到处散落,难以维护
- 团队各写各的,命名、分层不统一
- 改需求时,要翻很多目录才能定位到相关代码
这篇文章会围绕 Vue3 项目按业务域划分的目录结构规范,讲清楚:
- 为什么要这样分
- 每个目录该放什么、不该放什么
- 怎么从零搭建一个规范的目录
- 常见坑点如何规避
目标是:看完就能照着做,新人也能快速上手。
二、常见的"反面教材"
2.1 所有组件堆在 components 里
bash
src/
├── components/
│ ├── UserCard.vue # 用户相关
│ ├── OrderList.vue # 订单相关
│ ├── ProductDetail.vue # 商品相关
│ ├── LoginForm.vue # 登录相关
│ ├── Header.vue
│ ├── Footer.vue
│ └── ...(几十个组件混在一起)
问题:业务一多,components 会变成大杂烩,找文件、理清依赖都很费劲。
2.2 只按技术类型划分
bash
src/
├── views/ # 全是页面
├── components/ # 全是组件
├── api/ # 全是接口
├── utils/ # 全是工具函数
问题:和业务脱节。改"用户中心"要同时改 views、components、api,心智负担大。
2.3 嵌套过深
bash
src/
├── modules/
│ └── user/
│ └── profile/
│ └── settings/
│ └── security/
│ └── SecurityForm.vue # 层级太深
问题:导入路径过长,易出错,也不利于理解整体结构。
三、核心思路:按业务域划分
3.1 什么是"业务域"
业务域 = 产品里的一块独立功能/业务模块。例如:
- 用户:登录、注册、个人中心、设置
- 商品:列表、详情、搜索
- 订单:下单、列表、详情
- 营销:活动、优惠券
每个域包含:页面、组件、接口、类型、工具等,尽量聚合在一起。
3.2 这样划分的好处
| 好处 | 说明 |
|---|---|
| 易于定位 | 改"订单"就进 order 目录,不用到处找 |
| 易于协作 | 不同人负责不同域,冲突少 |
| 易于复用 | 模块边界清晰,抽离公共能力更自然 |
| 易于交接 | 新人看目录就能大致理解业务结构 |
4、推荐的 Vue3 项目目录结构(完整版)
python
src/
├── api/ # 全局 API(跨域、通用接口)
│ └── request.ts # 封装 axios / fetch
│
├── assets/ # 静态资源
│ ├── images/
│ ├── styles/
│ │ ├── variables.scss # 变量
│ │ └── global.scss # 全局样式
│ └── fonts/
│
├── components/ # 全局公共组件(多域复用)
│ ├── common/ # 通用 UI 组件
│ │ ├── AppButton.vue
│ │ ├── AppModal.vue
│ │ └── AppTable.vue
│ └── layout/ # 布局组件
│ ├── AppHeader.vue
│ ├── AppSidebar.vue
│ └── AppFooter.vue
│
├── composables/ # 全局可复用组合式函数
│ ├── useAuth.ts # 鉴权
│ ├── usePagination.ts # 分页
│ └── useTable.ts # 表格
│
├── router/
│ └── index.ts # 路由配置
│
├── store/ # Pinia 状态
│ └── index.ts # 根 store 或全局模块
│
├── types/ # 全局类型
│ └── common.ts
│
├── utils/ # 全局工具
│ ├── format.ts
│ ├── storage.ts
│ └── validate.ts
│
├── views/ # 路由页面(按业务域划分子目录)
│ ├── user/ # 用户域
│ │ ├── Login.vue
│ │ ├── Register.vue
│ │ ├── Profile.vue
│ │ └── components/ # 仅本域使用的组件
│ │ └── ProfileForm.vue
│ ├── product/ # 商品域
│ │ ├── List.vue
│ │ ├── Detail.vue
│ │ └── components/
│ │ └── ProductCard.vue
│ └── order/ # 订单域
│ ├── List.vue
│ ├── Detail.vue
│ └── components/
│ └── OrderStatus.vue
│
├── modules/ # 业务域模块(页面外的逻辑、API、类型)
│ ├── user/
│ │ ├── api.ts # 用户相关接口
│ │ ├── types.ts # 用户相关类型
│ │ └── store.ts # 用户相关 store(可选)
│ ├── product/
│ │ ├── api.ts
│ │ └── types.ts
│ └── order/
│ ├── api.ts
│ └── types.ts
│
├── App.vue
└── main.ts
4.1 各目录职责说明
| 目录 | 职责 | 放什么 | 不放什么 |
|---|---|---|---|
api/ |
请求封装、全局接口 | axios 实例、拦截器、跨域请求 | 具体业务接口 |
assets/ |
静态资源 | 图片、样式、字体 | 逻辑代码 |
components/ |
全局组件 | 多域复用 UI、布局 | 只在某一域用的组件 |
composables/ |
全局组合式函数 | 鉴权、分页、表格等 | 单页面内用的逻辑 |
router/ |
路由 | 路由配置 | 业务组件 |
store/ |
全局状态 | 跨域共享的状态 | 单域状态建议放 modules/xxx/store.ts |
types/ |
全局类型 | 通用 TS 类型 | 业务域专用类型 |
utils/ |
全局工具 | 格式化、校验、存储 | 业务逻辑 |
views/ |
路由页面 | 按业务域分组 | 非页面组件 |
modules/ |
业务域逻辑 | 接口、类型、单域 store | 视图组件 |
五、实战:按业务域搭建一个简单项目
5.1 目录规划
bash
views/user/Login.vue # 登录页
modules/user/api.ts # 登录接口
modules/user/types.ts # 用户类型
components/common/AppInput.vue # 通用输入框(若复用)
5.2 modules/user/types.ts
ts
// 用户相关类型定义
export interface LoginParams {
username: string
password: string
}
export interface UserInfo {
id: number
username: string
avatar: string
token: string
}
5.3 modules/user/api.ts
ts
// 用户相关接口 - 集中管理,方便维护
import request from '@/api/request'
import type { LoginParams, UserInfo } from './types'
export const loginApi = (data: LoginParams) => {
return request.post<UserInfo>('/api/user/login', data)
}
export const getUserInfoApi = (id: number) => {
return request.get<UserInfo>(`/api/user/${id}`)
}
5.4 views/user/Login.vue
html
<template>
<div class="login-page">
<h1>用户登录</h1>
<form @submit.prevent="handleLogin">
<input
v-model="form.username"
type="text"
placeholder="用户名"
/>
<input
v-model="form.password"
type="password"
placeholder="密码"
/>
<button type="submit" :disabled="loading">登录</button>
</form>
<p v-if="errorMsg" class="error">{{ errorMsg }}</p>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router'
import { loginApi } from '@/modules/user/api'
import type { LoginParams } from '@/modules/user/types'
const router = useRouter()
const loading = ref(false)
const errorMsg = ref('')
const form = reactive<LoginParams>({
username: '',
password: ''
})
const handleLogin = async () => {
if (!form.username || !form.password) {
errorMsg.value = '请填写完整信息'
return
}
loading.value = true
errorMsg.value = ''
try {
const res = await loginApi(form)
// 存储 token 等逻辑...
router.push('/profile')
} catch (e: any) {
errorMsg.value = e.message || '登录失败'
} finally {
loading.value = false
}
}
</script>
<style scoped>
.login-page {
max-width: 400px;
margin: 50px auto;
padding: 20px;
}
.error {
color: red;
font-size: 14px;
}
</style>
说明要点:
- 页面只负责「展示 + 调用接口」
- 接口在
modules/user/api.ts,类型在modules/user/types.ts - 修改接口或类型时,只需改对应模块,页面改动小
六、常见坑与避坑建议
6.1 组件该放 components/ 还是 views/xxx/components/?
- 只有当前业务域会用 → 放
views/xxx/components/ - 至少两个域会用到 → 放
components/common/或components/layout/
6.2 API 放哪?
- 只服务于某一域 →
modules/xxx/api.ts - 跨域或基础能力 →
api/或单独抽一层(如api/base.ts)
6.3 类型放哪?
- 仅该域使用 →
modules/xxx/types.ts - 多处复用 →
types/common.ts或共享的types/模块
6.4 路径别名
在 vite.config.ts 或 vue.config.js 里配置:
ts
// vite.config.ts 示例
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
}
这样可以用 @/modules/user/api 代替 ../../../modules/user/api。
6.5 循环依赖
modules/user/api.ts 引用 store,store 又引用 api,容易形成循环。建议:
- API 层不依赖 store
- store 调用 api,或通过 composable 间接调用
七、小结:如何落地这套规范
- 新项目:先按业务域规划
views/和modules/,再写代码。 - 老项目:可先从新功能开始,按域拆分,逐步迁移旧代码。
- 团队:把目录规范写到文档或
.cursor/rules里,新人 onboarding 时统一讲解。
核心原则:高内聚、低耦合 ------同一业务域的代码尽量在一起,跨域共用部分放到 components、utils、composables 等全局目录。
技术成长,从来不是比谁写得快,而是比谁写得稳、规范、可维护。
哪怕每次只吃透一条规范,长期下来,差距会非常明显。
后续我会持续更新前端规范、工程化、可维护代码相关实战干货,帮你告别面条代码、维护噩梦,在开发与面试中更有底气。
觉得有用欢迎 点赞 + 收藏 + 关注,不错过每一篇实战内容。
我是 Eugene,与你一起写规范、写优质代码,我们下篇干货见~