Vue3+ElementPlus前后端分离项目实战
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/, ''), } } } })这段配置做了什么?
配置项 | 作用 |
|---|---|
| 把 |
| 拦截所有以 |
| 转发到后端服务器地址 |
| 修改请求头中的 Origin,避免后端跨域拦截 |
| 去掉 |
举个例子
假设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 |
| 无请求体 |
查询单个 | GET |
| 路径参数 |
新增 | POST |
| data 在请求体中 |
修改 | PUT |
| data 在请求体中 |
删除 | DELETE |
| 查询参数 |
五、第三层: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}八、总结
通过这一个部门管理模块,我们看到了前后端分离项目的典型分层:
层级 | 文件 | 职责 |
|---|---|---|
视图层 |
| 页面渲染、用户交互、状态管理 |
请求层 |
| 封装 API、翻译业务操作为 HTTP 请求 |
配置层 |
| 开发代理、路径别名 |
后端 | Spring Boot(未展开) | 处理业务、操作数据库 |
核心思想:每一层只关心自己的事。组件不关心请求怎么发出去,API 层不关心数据怎么展示,代理不关心业务逻辑。各司其职,维护起来就清晰多了。(这就非常像我们后端的三层架构设计)
好啦,这次的分享到此结束,后面遇到好玩的东西我会继续分享,我们下期再见!!
