
Hello大家好我是Yize好久不见最近一直在完成JavaWeb的学习这两天在进行前端开发的时候发现了一个非常有意思的开发模式下面就跟我一起来学习吧Vue3 Element Plus 前后端分离项目从页面到后端如何进行一、前言在前后端分离开发中和我一样的很多初学者会遇到这样的困惑我点了页面上的新增按钮数据是怎么跑到数据库里的proxy配置到底是干嘛的不配行不行为什么我的请求 404 了下面将根据我最近在完成的Tlias智能学习辅助系统为例把前端页面、API 请求层、Vite 代理配置三段代码串联起来带领大家彻底搞懂前后端分离项目的运行全貌。二、项目结构概览我们的部门管理模块涉及三个关键文件project/ ├── vite.config.js # Vite 配置含代理 ├── src/ │ ├── api/ │ │ └── depts.js # API 请求层 │ ├── views/ │ │ └── depts.vue # 部门管理页面组件 │ └── utils/ │ └── request.js # axios 封装本文不展开三层架构关系┌────────────────────────────────────────────────────┐ │ Vue 组件 (depts.vue) │ │ ↑ 调用 ↓ 渲染 │ │ API 层 (depts.js) │ │ ↑ 发送请求 │ │ Vite Proxy │ │ ↓ 转发 │ │ 后端服务器 (localhost:8080) │ └────────────────────────────────────────────────────┘三、第一层Vite 代理配置先看vite.config.js中的核心配置export default defineConfig({ plugins: [vue()], resolve: { alias: { : fileURLToPath(new URL(./src, import.meta.url)) } }, server: { proxy: { /api: { target: http://localhost:8080, secure: false, changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ), } } } })这段配置做了什么配置项作用alias: { : ... }把映射为src/目录写导入路径时可以写/api/depts而不用写相对路径/api拦截所有以/api开头的请求target转发到后端服务器地址changeOrigin修改请求头中的 Origin避免后端跨域拦截rewrite去掉/api前缀后再转发举个例子假设request.js中配置了baseURL: /api前端请求: /api/depts ↓ Vite 代理转发 实际到达: http://localhost:8080/depts为什么要代理 开发环境下前端跑在localhost:5173后端跑在localhost:8080不同端口会产生跨域问题。通过 Vite 代理浏览器只和5173通信由 Vite 服务端转发请求到8080绕过了浏览器的同源策略。四、第二层API 请求层src/api/depts.js负责把业务操作翻译成 HTTP 请求import request from /utils/request.js // 查询全部部门 export const queryAllApi () request.get(/depts) // 添加部门 export const addDeptApi (data) request.post(/depts, data) // 根据ID查询 export const queryInfoApi (id) request.get(/depts/${id}) // 修改部门 export const updateDeptApi (data) request.put(/depts, data) // 删除部门 export const deleteDeptApi (id) request.delete(/depts?id${id})RESTful 风格一览业务操作HTTP 方法请求路径说明查询全部GET/depts无请求体查询单个GET/depts/{id}路径参数新增POST/deptsdata 在请求体中修改PUT/deptsdata 在请求体中删除DELETE/depts?id{id}查询参数五、第三层Vue 组件业务控制层depts.vue是整个页面的核心我们用script setup语法编写5.1 数据定义与初始化import { ref, onMounted } from vue import { queryAllApi, addDeptApi, queryInfoApi, updateDeptApi, deleteDeptApi } from /api/depts // 表格数据 let deptList ref([]) // 页面挂载时加载数据 onMounted(() { queryAll() }) const queryAll async () { const result await queryAllApi() deptList.value result.data }运行流程组件挂载(onMounted) → 调用 queryAll() → await queryAllApi() // 发送 GET /depts → 后端返回 { code: 200, data: [...] } → deptList.value result.data → 响应式更新表格自动渲染5.2 新增部门const add () { formTitle.value 新增部门 showDialog.value true deptForm.value { name: } }用户点击确定后的提交流程const save async () { await deptFormRef.value.validate(async valid { if (!valid) return let result null if (deptForm.value.id) { result await updateDeptApi(deptForm.value) // 修改 } else { result await addDeptApi(deptForm.value) // 新增 } if (result.code) { ElMessage.success(操作成功) showDialog.value false resetForm() queryAll() // 刷新列表 } else { ElMessage.error(result.msg) } }) }关键点通过判断deptForm.value.id是否存在来区分新增和修改复用同一个对话框和提交方法。5.3 修改部门const handleEdit async (id) { formTitle.value 修改部门 showDialog.value true deptForm.value { name: } const result await queryInfoApi(id) if (result.code) { deptForm.value result.data // 回显数据 } }回显逻辑先打开弹窗再根据 ID 查询完整数据填充表单用户修改后点确定走save()方法。5.4 删除部门const handleDelete (id) { ElMessageBox.confirm(此操作将永久删除该部门, 是否继续?, 提示, { confirmButtonText: 确定, cancelButtonText: 取消, type: warning }).then(async () { const result await deleteDeptApi(id) if (result.code) { ElMessage.success(删除成功) queryAll() // 刷新列表 } }) }先用ElMessageBox弹确认框用户确认后才真正发删除请求。六、完整调用链路图以修改部门为例完整的数据流转如下┌─────────────────────────────────────────────────────────────┐ │ ① 用户点击修改按钮 │ │ → handleEdit(id) │ ├─────────────────────────────────────────────────────────────┤ │ ② 调用 API 层 │ │ → queryInfoApi(id) │ │ → GET /depts/1 │ ├─────────────────────────────────────────────────────────────┤ │ ③ Vite 代理转发 │ │ → /api/depts/1 → http://localhost:8080/depts/1 │ ├─────────────────────────────────────────────────────────────┤ │ ④ 后端处理 │ │ → Controller 接收请求 │ │ → Service 调用业务逻辑 │ │ → Mapper 查询数据库 │ │ → 返回 { code: 200, data: { id: 1, name: 研发部 } } │ ├─────────────────────────────────────────────────────────────┤ │ ⑤ 前端接收响应 │ │ → deptForm.value result.data │ │ → 表单回显研发部 │ ├─────────────────────────────────────────────────────────────┤ │ ⑥ 用户修改名称点击确定 │ │ → save() → updateDeptApi(data) │ │ → PUT /depts → 后端更新数据库 │ │ → queryAll() → 表格刷新 │ └─────────────────────────────────────────────────────────────┘七、常见坑点与最佳实践7.1 代理不生效现象请求直接打到前端端口报 404。排查检查request.js中是否配置了baseURL: /api检查 API 层路径是否以/depts开头不带/api两者必须配套否则代理匹配不到/api规则7.2 跨域问题现象浏览器控制台报 CORS 错误。解决开发环境确保 Vite 代理配置正确生产环境让后端配置 CORS 或通过 Nginx 反向代理7.3 代码风格统一建议// ❌ 不一致的风格 GET /depts/{id} // 路径参数 DELETE /depts?id{id} // 查询参数 // ✅ 统一 RESTful 风格 GET /depts/{id} PUT /depts/{id} DELETE /depts/{id}八、总结通过这一个部门管理模块我们看到了前后端分离项目的典型分层层级文件职责视图层depts.vue页面渲染、用户交互、状态管理请求层depts.js封装 API、翻译业务操作为 HTTP 请求配置层vite.config.js开发代理、路径别名后端Spring Boot未展开处理业务、操作数据库核心思想每一层只关心自己的事。组件不关心请求怎么发出去API 层不关心数据怎么展示代理不关心业务逻辑。各司其职维护起来就清晰多了。这就非常像我们后端的三层架构设计好啦这次的分享到此结束后面遇到好玩的东西我会继续分享我们下期再见