Vue3+ElementPlus前后端分离项目实战

📅 2026/8/2 6:32:39 👁️ 阅读次数 📝 编程学习
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/, ''), } } } })

这段配置做了什么?

配置项

作用

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

/depts

data 在请求体中

修改

PUT

/depts

data 在请求体中

删除

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 层不关心数据怎么展示,代理不关心业务逻辑。各司其职,维护起来就清晰多了。(这就非常像我们后端的三层架构设计)

好啦,这次的分享到此结束,后面遇到好玩的东西我会继续分享,我们下期再见!!