当前位置: 首页 > news >正文

Spring Boot + Vue3 前后端分离实践

前言在传统的Web开发中我们通常使用JSP、Thymeleaf等模板引擎将前端和后端代码混合在一起。这种开发方式在项目规模较小时还够用但随着项目复杂度的增加前后端代码耦合严重、开发效率低下、维护困难等问题逐渐暴露出来。前后端分离架构应运而生它通过将前端和后端完全解耦让前后端工程师可以独立开发、独立部署大大提高了开发效率和项目可护性。本文将以我开发的「超市货品管理系统」为例详细介绍如何使用 Spring Boot Vue3 实现前后端分离开发。项目背景超市货品管理系统是一个面向超市日常运营的管理系统主要功能包括用户认证、商品管理、图片管理、供货商管理等模块。采用前端分离架构后端提供RESTful API前端负责页面展示和交互。技术选型后端技术栈- Spring Boot 2.7.18快速构建应用简化配置- MyBatis Plus 3.5.5简化数据库操作提供CRUD通用接口- MySQL 8.0关系型数据库存储业务数据- JWT 0.11.5实现无状态认证- Knife4j 3.0.3自动生成API文档前端技术栈- Vue 3.4渐进式JavaScript框架- Vite 5.1新一代前端构建工具开发体验极佳- Vue Router 4.3官方路由管理器- Pinia 2.1Vue3官方推荐的状态管理库- Element Plus 2.6Vue3组件库- Axios 1.6HTTP客户端项目架构整体架构图┌─────────────┐│ Browser │└──────┬──────┘│▼┌─────────────┐│ Vue3 Front ││ (SPA) │└──────┬──────┘│ HTTP/Axios▼┌─────────────┐│ Nginx/Dev ││ Server │└──────┬──────┘│▼┌─────────────┐│ Spring Boot ││ Backend │└──────┬──────┘│▼┌─────────────┐│ MySQL DB │└─────────────┘后端项目结构backend/├── src/main/java/com/supermarket/│ ├── config/ # 配置类CORS、MyBatis│ ├── controller/ # 控制器层│ ├── dto/ # 数据传输对象│ ├── entity/ # 实体类│ ├── mapper/ # MyBatis Mapper│ ├── service/ # 服务层│ ├── util/ # 工具类│ └── vo/ # 视图对象└── src/main/resources/├── mapper/ # MyBatis XML└── application.yml # 配置文件前端项目结构frontend/├── src/│ ├── api/ # API接口封装│ ├── assets/ # 静态资源│ ├── components/ # 公共组件│ ├── router/ # 路由配置│ ├── stores/ # Pinia状态管理│ ├── utils/ # 工具函数│ ├── views/ # 页面组件│ ├── App.vue # 根组件│ └── main.js # 入口文件├── index.html # HTML模板├── vite.config.js # Vite配置└── package.json # 依赖管理后端开发实践1. 统一响应结果封装为了统一接口返回格式我们定义了一个通用的Result类package com.supermarket.util;import lombok.Data;Datapublic class ResultT {private Integer code;private String message;private T data;public static T ResultT success(T data) {ResultT result new Result();result.setCode(200);result.setMessage(操作成功);result.setData(data);return result;}public static T ResultT error(String message) {ResultT result new Result();result.setCode(500);result.setMessage(message);return result;}}这样所有接口都返回统一的JSON格式{code: 200,message: 操作成功,data: { }}2. RESTful API设计遵循RESTful规范设计API接口┌──────────┬─────────────────┬──────────┐│ HTTP方法 │ URL │ 说明 │├──────────┼─────────────────┼──────────┤│ GET │ /api/goods/page │ 分页查询 │├──────────┼─────────────────┼──────────┤│ GET │ /api/goods/{id} │ 查询详情 │├──────────┼─────────────────┼──────────┤│ POST │ /api/goods │ 创建资源 │├──────────┼─────────────────┼──────────┤│ PUT │ /api/goods/{id} │ 更新资源 │├──────────┼─────────────────┼──────────┤│ DELETE │ /api/goods/{id} │ 删除资源 │└──────────┴─────────────────┴──────────┘Controller示例Api(tags 商品管理)RestControllerRequestMapping(/goods)public class GoodsController {Resourceprivate GoodsService goodsService;ApiOperation(value 分页查询商品)GetMapping(/page)public ResultIPageGoodsVO getGoodsPage(RequestParam(defaultValue 1) Integer pageNum,RequestParam(defaultValue 10) Integer pageSize,RequestParam(required false) String keyword) {IPageGoodsVO page goodsService.getGoodsPage(pageNum, pageSize, keyword);return Result.success(page);}ApiOperation(value 创建商品)PostMappingpublic ResultGoodsVO createGoods(RequestBody Validated GoodsDTO goodsDTO) {GoodsVO goodsVO goodsService.createGoods(goodsDTO);return Result.success(goodsVO);}}3. JWT认证实现JWTJSON Web Token是一种无状态的认证机制适合前后端分离架构。JWT工具类Componentpublic class JwtUtil {Value(${jwt.secret})private String secret;Value(${jwt.expiration})private Long expiration;public String generateToken(Long userId, String username) {MapString, Object claims new HashMap();claims.put(userId, userId);claims.put(username, username);Date now new Date();Date expiryDate new Date(now.getTime() expiration);return Jwts.builder().setClaims(claims).setIssuedAt(now).setExpiration(expiryDate).signWith(Keys.hmacShaKeyFor(secret.getBytes()), SignatureAlgorithm.HS512).compact();}public boolean validateToken(String token) {try {Claims claims Jwts.parserBuilder().setSigningKey(Keys.hmacShaKeyFor(secret.getBytes())).build().parseClaimsJws(token).getBody();return !claims.getExpiration().before(new Date());} catch (Exception e) {return false;}}}登录接口PostMapping(/login)public ResultLoginVO login(RequestBody LoginDTO loginDTO) {User user userService.getByUsername(loginDTO.getUsername());if (user null || !user.getPassword().equals(loginDTO.getPassword())) {throw new BusinessException(账号或密码错误);}String token jwtUtil.generateToken(user.getId(), user.getUsername());String refreshToken jwtUtil.generateRefreshToken(user.getId(), user.getUsername());LoginVO loginVO new LoginVO();loginVO.setToken(token);loginVO.setRefreshToken(refreshToken);loginVO.setUserInfo(user);return Result.success(loginVO);}4. CORS跨域配置前后端分离开发时前端localhost:3000和后端localhost:8080端口号不同会产生跨域问题。Configurationclass WebConfig implements WebMvcConfigurer {Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping(/**).allowedOriginPatterns(*).allowedMethods(GET, POST, PUT, DELETE, OPTIONS).allowedHeaders(*).allowCredentials(true).maxAge(3600);}}前端开发实践1. Axios请求封装统一封装axios实例添加请求和响应拦截器import axios from axiosimport { ElMessage } from element-plusimport { useUserStore } from /stores/userimport router from /routerconst request axios.create({baseURL: /api,timeout: 30000})// 请求拦截器自动添加Tokenrequest.interceptors.request.use(config {const userStore useUserStore()if (userStore.token) {config.headers.Authorization userStore.token}return config},error Promise.reject(error))// 响应拦截器统一处理错误request.interceptors.response.use(response {const res response.dataif (res.code 200) {return res.data} else {ElMessage.error(res.message || 请求失败)return Promise.reject(new Error(res.message))}},error {if (error.response?.status 401) {ElMessage.error(登录已过期请重新登录)const userStore useUserStore()userStore.logout()router.push(/login)}return Promise.reject(error)})export default request2. API模块化管理将所有API接口按模块组织// src/api/goods.jsimport request from /utils/requestexport const getGoodsPage (params) {return request({url: /goods/page,method: get,params})}export const createGoods (data) {return request({url: /goods,method: post,data})}export const deleteGoods (id) {return request({url: /goods/${id},method: delete})}3. 状态管理Pinia使用Pinia管理全局状态如用户信息// src/stores/user.jsimport { defineStore } from piniaimport { ref } from vueexport const useUserStore defineStore(user, () {const token ref(localStorage.getItem(token) || )const userInfo ref(null)const setToken (newToken) {token.value newTokenlocalStorage.setItem(token, newToken)}const setUserInfo (info) {userInfo.value info}const logout () {token.value userInfo.value nulllocalStorage.removeItem(token)}return { token, userInfo, setToken, setUserInfo, logout }})4. 路由守卫实现路由权限控制// src/router/index.jsimport { createRouter, createWebHistory } from vue-routerimport { useUserStore } from /stores/userconst routes [{ path: /login, component: () import(/views/Login.vue) },{ path: /, component: () import(/views/Layout.vue),children: [{ path: goods, component: () import(/views/Goods.vue) }]}]const router createRouter({history: createWebHistory(),routes})router.beforeEach((to, from, next) {const userStore useUserStore()if (to.path ! /login !userStore.token) {next(/login)} else {next()}})export default router前后端联调1. 开发环境代理配置在Vite配置开发代理解决跨域问题export default defineConfig({server: {port: 3000,proxy: {/api: {target: http://localhost:8080,changeOrigin: true},/upload: {target: http://localhost:8080,changeOrigin: true}}}})这样前端请求 /api/xxx 会被代理到 http://localhost:8080/api/xxx。2. API文档集成使用Knife4j自动生成API文档启动后端服务后访问 http://localhost:8080/api/doc.html 即可查看。文档包含- 接口列表和说明- 请求参数和响应格式- 在线调试功能3. 接口联调流程1. 后端先开发接口用Knife4j测试通过2. 前端根据API文档编写接口封装3. 前端调用接口查看响应数据4. 调整数据格式和展示逻辑5. 异常情况处理网络错误、权限错误等遇到的问题和解决方案1. 跨域问题问题前端请求后端接口报错 No Access-Control-Allow-Origin header解决方案- 开发环境配置Vite代理- 生产环境配置Spring Boot CORS2. Token过期处理问题用户长时间未操作Token过期导致请求失败解决方案- 响应拦截器统一处理401错误- 自动跳转到登录页面- 清除本地Token和用户信息3. 文件上传问题图片上传失败路径配置错误解决方案- 配置静态资源映射registry.addResourceHandler(/upload/**).addResourceLocations(file:F:/upload/picture/);- 确保上传目录存在且有写入权限- 前端使用FormData上传4. 分页查询问题MyBatis Plus分页查询不生效解决方案- 添加MyBatis Plus分页配置Configurationpublic class MybatisPlusConfig {Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor());return interceptor;}}总结与展望通过这次前后端分离实践我深刻体会到1. 开发效率提升前后端并行开发互不干扰2. 代码可维护性职责分离结构清晰3. 技术栈独立前端和后端可以使用不同的技术4. 便于部署扩展可以独立部署和扩展未来优化方向- 引入Redis缓存提升系统性能- 使用Docker容器化部署- 添加日志监控系统- 实现接口版本管理- 前端添加单元测试前后端分离已经成为现代Web开发的主流架构掌握这种架构对于每个开发者来说都是必备技能。希望这篇文章能够帮助到正在学习前后端分离的同学。
http://www.rkmt.cn/news/1385791.html

相关文章:

  • seq2seq架构——为transformer奠基
  • Sora 2 HDR视频生成落地指南:3步完成BT.2100 PQ曲线对齐、17项HDR元数据校验、5类常见色带伪影修复
  • 元学习MAML结合物理信息神经网络,破解小样本交通流预测难题
  • Midjourney锐化效果失效真相(2024官方未公开的渲染管线瓶颈解析)
  • 终极鼠标连点器使用指南:3分钟掌握高效自动化技巧
  • 为什么92%的Lindy自动化项目半年内失效?深度复盘4类致命设计缺陷及修复清单
  • 【Midjourney烟雾效果终极指南】:20年视觉算法专家亲授7种工业级烟雾渲染技法,90%用户从未见过的隐藏参数组合!
  • 【DeepSeek开源协议识别权威指南】:20年合规专家亲授3大协议陷阱与5步精准识别法
  • 潮州东方轻奢风全屋高定找哪家
  • 从Dark Channel Prior到AOD-Net:手把手带你复现5个经典图像去雾算法(Python/PyTorch)
  • 竞赛题解题方法
  • 2026年道路波形护栏TOP5企业推荐:省道波形护栏/路侧护栏板/镀锌护栏板/镀锌波形护栏/防撞护栏板/防撞波形护栏/选择指南 - 优质品牌商家
  • DeepSeek+DDD融合架构设计:从Prompt边界建模到智能体领域事件流编排(独家方法论首发)
  • 123546
  • PIML技术提升CFD湍流模拟精度:从数据驱动到工程应用实践
  • Sora 2导出MP4黑屏/绿屏/元数据丢失?99.2%复现率的QuickTime兼容性漏洞已确认,3种紧急绕行方案今日限时公开
  • 7.力扣【三数之和】史上最清晰双指针解法!三步搞定,面试必看!
  • 基于YOLO+InsightFace(ArcFace)的人脸识别检测系统
  • 如何快速解密QQ音乐加密文件:macOS用户的终极音频格式转换方案
  • 2026年高压开关测试仪优质产品推荐榜:便携式三相电能质量分析仪、开关参数测试仪、开关特性试验仪、手持式三相电能质量分析仪选择指南 - 优质品牌商家
  • 中兴光猫配置解密终极指南:5步掌握ZET-Optical-Network-Terminal-Decoder核心技术
  • Python PIL 画矩形框
  • 3分钟掌握城通网盘解析:告别缓慢下载的完整解决方案
  • 当游戏语言成为障碍:XUnity.AutoTranslator如何让外语游戏秒变中文
  • 2026年5月更新:如何甄选温州地区真正靠谱的商务笔记本生产合作伙伴 - 2026年企业推荐榜
  • 接水管游戏背后的状态传播引擎设计原理
  • 大模型降价的工程极限:从DeepSeek-V4-Pro看AI推理的成本革命
  • 给嵌入式新人的AUTOSAR入门指南:从MCU选型到主流方案(附Vector/EB/ETAS对比)
  • 吴恩达免费AI新课:真正适合普通人的课程
  • 3分钟拯救废稿:Midjourney一键锐化增强术(含--no watermarks规避+局部重绘锚点定位技巧)