资讯动态

Vue 3 + SpringBoot前后端分离项目实战:从搭建到部署全流程

发布时间:2026/9/20 21:00:36 来源:尧图企业网站定制
简介一份面向初中级开发者的前后端分离实战资源基于Vue 3与Element Plus构建前端界面后端采用Spring Boot 3并整合Spring Security、JWT、Redis及文件上传下载等常用能力适合希望快速掌握企业级项目搭建流程的学习者。资源共2000个文件其中以1811个JavaScript文件为主辅以Java、JSON、XML及SQL脚本分别对应前端页面逻辑、后端服务代码、配置文件与数据库表结构压缩包整体22.59MB便于下载与检索。目前已有98人学习可作为从零搭建VueSpringBoot项目的参考模板。除核心源码外还包含数据库设计、接口设计和安全性设计思路能够帮助读者理解JWT认证机制、Redis缓存应用、文件传输实现等关键环节具备较强的实战参考价值。1. 项目概述与整体设计1.1 这个项目到底在做什么说句实在话前后端分离这个词已经被念叨了好几年但真到了自己动手从零搭一个 Vue SpringBoot 项目的时候不少朋友还是会被各种细节卡住。这个项目本身并不复杂——前端用 Vue 3 Vite后端用 SpringBoot 2.7 MyBatis-Plus实现一套最典型的用户登录注册 数据列表查询 文件上传下载的小系统。但麻雀虽小五脏俱全它能完整覆盖从开发到联调再到部署的一条链路。我之所以选这个组合来做实例是因为 Vue 和 SpringBoot 是目前国内中小团队使用率最高的前后端技术栈。Vue 上手曲线平缓、中文文档齐全、生态丰富SpringBoot 则让 Java 后端的开发变得异常轻量内置 Tomcat、自动配置、起步依赖这些特性几乎就是为快速交付业务接口而生的。两个东西加在一起能解决的问题非常广企业内部管理系统、电商后台、内容管理平台、小程序管理端基本上都能用这套骨架去套。适合谁来参考呢我建议有三类人认真看完第一类是刚学完 Vue 和 SpringBoot 基础语法、想做一个完整项目练手的同学第二类是公司里被迫从零搭建前后端分离工程、但没人带的新人开发第三类是准备面试、需要把前后端交互链路讲清楚的朋友。这篇内容不是教科书是我实际开发中一步步踩出来的经验总结跟着做能少走很多弯路。1.2 为什么选择 Vue 3 Vite SpringBoot 这套组合选型这事情没有绝对的最好只有适合当前场景的方案。我最初也纠结过要不要用 Vue 2毕竟公司老项目里 Vue 2 的存量很大。但考虑到 Vue 3 的组合式 APIComposition API确实让逻辑复用变得干净很多而且 Vite 带来的冷启动速度提升是肉眼可见的——以前用 Vue CLI 启动一个中型项目要等二三十秒换成 Vite 之后基本秒开。对于开发体验来说这个差距太明显了。后端用 SpringBoot 2.7 而非 3.x主要考虑的是生态兼容性。很多常用的第三方库比如一些工作流引擎、代码生成器在 SpringBoot 3.x 上还在适配期而 2.7 是 2.x 系列的最后一个稳定版本坑相对少得多。如果你的项目刚起步、不需要用那些冷门库直接上 3.x 也没问题但作为一个教学型实例我选择更稳妥的版本。数据库方面我用的是 MySQL 8.0 MyBatis-Plus。MyBatis-Plus 对单表 CRUD 的增强非常实用不需要手写 XML 就能完成大部分数据操作非常适合快速搭建原型项目。同时我还配置了 MyBatis-Plus 的自动建表功能——这个功能在某些场景下极其好用比如演示环境、测试环境、快速交付演示项目时完全不需要手动去执行 SQL 脚本。整个项目的前后端交互走的是 RESTful API JSON前端开发时通过 Vite 的代理Proxy解决跨域问题生产环境则用 Nginx 做反向代理将/api路径的请求转发到后端服务。这套方案是我在多个生产项目中验证过的稳定性和可维护性都很好。2. 前端 Vue 部分的关键搭建与实现2.1 Vue 安装及环境配置的注意事项很多新手在第一步就栽跟头——不是 Node 版本不对就是 npm 源太慢导致依赖安装失败。我先说一下我自己比较推荐的配置流程。安装 Node.js 的时候尽量选择 LTS 版本。不要追新有些 Vite 插件在最新的 Node 奇数版本上可能存在兼容性问题。装完 Node 顺手把 npm 镜像切到国内源npm config set registry https://registry.npmmirror.com这一步能节省大量等待时间实测下来安装依赖的速度能快三倍以上。创建项目我推荐用 Vite 的官方脚手架npm create vitelatest frontend -- --template vue这里解释一下为什么要用 Vite 而不是 Vue CLI。Vite 基于原生 ES Module开发模式下不需要打包整个应用而是按需编译浏览器请求的模块所以启动速度和热更新速度都非常快。我印象最深的一次是一个项目有三百多个组件用 Vue CLI 改一行代码要等两秒左右才能看到热更新效果换成 Vite 之后几乎是即时刷新。创建完成后进入项目目录安装路由和状态管理相关的依赖。这里我用了 Vue Router 4 和 Pinianpm install vue-router4 pinia然后安装 UI 组件库。如果做后台管理系统推荐 Ant Design Vue如果做面向用户的网站推荐 Element Plus。这个项目我选的是 Element Plus因为它的表格、表单、弹窗组件写起来非常顺手。npm install element-plus element-plus/icons-vue这里有一个我自己总结的小经验Element Plus 的图标需要单独安装而且如果按需引入需要在 main.js 里把所有用到的图标统一注册。很多人忘了这一步结果页面上图标显示不出来排查半天才发现是注册的问题。2.2 路由设计从登录页到业务页面的完整跳转逻辑路由是前端项目的骨架我见过很多项目路由写得很随意结果后面加权限控制的时候痛不欲生。这里我推荐一套清晰的路由组织方式。在src/router/index.js下定义路由至少要有两种路由基础路由和业务路由。基础路由包括登录页、404 页面业务路由放在一个统一的 Layout 布局组件之下方便统一加载导航栏和侧边栏。import { createRouter, createWebHistory } from vue-router const routes [ { path: /login, name: Login, component: () import(../views/Login.vue) }, { path: /, component: () import(../layout/Index.vue), redirect: /dashboard, children: [ { path: dashboard, name: Dashboard, component: () import(../views/dashboard/index.vue) }, { path: user, name: UserList, component: () import(../views/user/index.vue) } ] } ] const router createRouter({ history: createWebHistory(), routes }) // 全局前置守卫未登录跳转到登录页 router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (!token to.path ! /login) { next(/login) } else { next() } }) export default router路由懒加载这个点要重点提一下——用() import()的方式导入组件Vite 会自动把每个页面拆成独立的 chunk首屏只加载当前页面需要的代码。之前有个项目没有做懒加载首屏体积到了 2MB 多白屏时间差不多两秒改成懒加载之后首屏压到了 500KB 以内体验提升非常明显。还有一个细节是路由模式。我这里用的是createWebHistory也就是 HTML5 History 模式URL 看起来是http://xxx.com/user而不是http://xxx.com/#/user。但要注意使用 History 模式后线上的 Nginx 需要配置try_files把所有路径都重定向到index.html否则刷新页面会报 404。这个在后文部署部分会细说。2.3 登录注册模块与后端接口的第一次握手登录注册是整个前后端项目里第一次真正意义上的数据交互也是很多人第一次被跨域问题卡住的地方。我先说我前端这边的写法。登录页模板里放一个表单两个字段用户名、密码加一个登录按钮。提交的时候调用封装好的登录接口import request from ../utils/request export function login(data) { return request({ url: /api/auth/login, method: post, data }) }这里的request是基于 Axios 封装的一个实例统一配置了 baseURL 和拦截器。重点说一下响应拦截器的写法它决定了你在组件里代码能少写多少import axios from axios import { ElMessage } from element-plus const request axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || /api, timeout: 10000 }) // 请求拦截器统一携带 token request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) // 响应拦截器统一处理异常 request.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res }, error { ElMessage.error(error.response?.data?.message || 网络异常) return Promise.reject(error) } ) export default request这样做的好处是组件里调用接口只需要关心返回的数据不需要每写一个接口都处理一遍code ! 200的逻辑分支。在登录页面拿到后端返回的 token 后存到 localStorage然后调用router.push(/dashboard)跳转到首页。整个交互链路就完成了。3. 后端 SpringBoot 部分的搭建与核心接口开发3.1 快速创建一个 SpringBoot 项目避免超时问题的三个办法后端的项目创建我一般用 IDEA 的 Spring Initializr但很多人都会遇到一个很烦的问题——创建项目时连接 start.spring.io 超时转半天圈然后报错。这里讲三个我自己验证过的方法。第一个方法改 IDEA 的 HTTP 代理设置让它能顺利访问外部网络。但这办法受限于公司网络策略有些环境下不好使。第二个方法使用国内的 Spring Initializr 镜像。阿里云提供了一个地址是https://start.aliyun.com。在 IDEA 的 Server URL 处替换掉默认地址创建速度就是一个字——快。需要注意的是阿里云镜像里提供的依赖版本可能会比官方源旧一点但对大多数项目没有影响。第三个方法是离线创建。在 IDEA 的 Maven 配置里设置好本地仓库路径然后自己手动在项目里添加pom.xml最后右键选择 Add as Maven Project。这种方式适合网络环境极端恶劣的情况。我平时最常用的是第二个方法基本上选好 Spring Web、MyBatis-Plus、MySQL Driver、Lombok 这几个依赖十几秒项目就创建完了。3.2 核心依赖与配置文件yml 里的静态资源和密码处理经验项目创建好之后第一步是改配置文件。我习惯把默认的application.properties改成application.yml因为 YAML 的层级结构更清晰尤其是配置多数据源、Redis 这类需要嵌套的信息时可读性高得多。一个需要注意的地方是 SpringBoot 2.7 以后的配置项变更。比如spring.redis改成了spring.data.redis以及 MyBatis 配置里的mapper-locations路径写法。版本不同配置不同搜资料的时候要先确认自己用的版本不然被旧资料坑了会花很多时间调莫名其妙的问题。server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/demo_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root password: ${DB_PASSWORD:123456} mybatis-plus: configuration: map-underscore-to-camel-case: true global-config: db-config: table-prefix: t_关于密码配置我多说一句。生产环境极度不建议把数据库密码明文写在 yml 里这属于安全底线问题。SpringBoot 本身没有内置的加密方案但可以引入 jasypt-spring-boot-starter对配置文件中的密码进行加密。基本用法是在配置项外面套一层ENC()标记然后在启动参数里传入解密密钥password: ENC(加密后的密文)启动时java -jar demo.jar --jasypt.encryptor.password你的密钥这套方案在 demo 里可以不搞但放到公司项目里建议一定要加上。安全问题越早考虑后面成本越低。3.3 表结构自动创建MyBatis-Plus 中的建表工具类实现这个项目的数据表很少总共就用户表、日志表几张所以我没有引入专门的数据库迁移工具比如 Flyway而是写了一个简单的表结构自动创建工具类。原理其实很简单项目启动时读取一个schema.sql文件检查数据库中是否存在目标表如果不存在就执行建表语句。实现方式是在 SpringBoot 的 ApplicationRunner 启动类里做处理Component public class TableInitRunner implements ApplicationRunner { Resource private JdbcTemplate jdbcTemplate; Override public void run(ApplicationArguments args) { String checkTableSql SELECT COUNT(*) FROM information_schema.tables WHERE table_schema demo_db AND table_name t_user; Integer count jdbcTemplate.queryForObject(checkTableSql, Integer.class); if (count null || count 0) { executeSqlScript(new ClassPathResource(sql/schema.sql)); } } }这样带来的好处是很直观的——把项目拉下来运行不必手动去 Navicat 里执行建表 SQL前后端同学都省心。尤其是快速搭建演示环境的时候这个细节能省掉至少十分钟的沟通成本。不过要说清楚这种方案只适合表结构不频繁变动的场景。如果项目进入迭代期表字段经常调整还是得回归 Flyway 或者 Liquibase否则增量 SQL 的执行顺序会成为新的麻烦。3.4 用户登录接口从 Controller 到 Service 的完整代码路径后端接口开发我遵循三层结构Controller接收请求、Service业务逻辑、Mapper数据访问。以登录接口为例代码路径大概是这样的。首先是 Controller 层RestController RequestMapping(/api/auth) public class AuthController { Resource private AuthService authService; PostMapping(/login) public ResultVOString login(RequestBody LoginDTO loginDTO) { String token authService.login(loginDTO); return ResultVO.success(token); } }然后是 Service 层。注意这里有一个细节——我用了ResultVO统一包装返回结果结构是{ code, message, data }这样的格式。这样前后端约定好了交互协议前端拦截器里的res.code ! 200判断才有意义。Service public class AuthServiceImpl implements AuthService { Resource private UserMapper userMapper; Override public String login(LoginDTO loginDTO) { // 1. 查询用户 User user userMapper.selectByUsername(loginDTO.getUsername()); // 2. 校验密码BCrypt加密存储 if (user null || !BCrypt.checkpw(loginDTO.getPassword(), user.getPassword())) { throw new BizException(用户名或密码错误); } // 3. 生成token并返回 return JwtUtil.generateToken(user.getId(), user.getUsername()); } }密码为什么一定要用 BCrypt 加密存储因为 MD5 撞库太容易了反查一个弱密码几乎是秒级的事。BCrypt 自带随机盐能有效对抗彩虹表攻击而且计算成本可以调整这是目前业界比较推荐的密码存储方案。用户表结构很简单对应字段就是 id、username、password、created_time 几个通过 MyBatis-Plus 的注解就能完成映射Data TableName(t_user) public class User { TableId(type IdType.AUTO) private Long id; private String username; private String password; TableField(created_time) private LocalDateTime createdTime; }3.5 跨域问题解析前后端分离后最常踩的一个坑跨域问题我单独拿出来说因为十个前后端联调项目里有九个会被它卡一下。跨域的本质是浏览器的同源策略——只有当请求的协议、域名、端口都一样时浏览器才允许 JavaScript 读取响应。在开发环境下前端跑在http://localhost:5173后端跑在http://localhost:8080端口不同浏览器就会拦截跨域请求。三种解决方案我都用过说下各自的适用场景。第一种是后端开 CORS 全局配置。在 SpringBoot 里写一个配置类Configuration public class CorsConfig { Bean public CorsFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); config.addAllowedOriginPattern(*); config.addAllowedMethod(*); config.addAllowedHeader(*); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsFilter(source); } }这种方案配置简单适合生产环境前后端域名不同的情况。但我个人不太建议在生产环境把允许来源设为*最好显式写成你前端的域名特别是如果网站要存 Cookie 做会话保持*会导致前端请求无法携带凭证必须精确指定来源。第二种是前端开发环境下的代理方案。在 Vite 的vite.config.js里配置export default defineConfig({ server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })这样前端发请求时浏览器看到的请求目标是http://localhost:5173/api/auth/login同源不触发跨域。Vite 把请求转发给后端的http://localhost:8080/api/auth/login。这是开发环境最推荐的方式不需要后端做任何额外配置同时还能灵活地切换后端地址。第三种是生产环境用 Nginx 反向代理。这种方式本质上和第二种类似只是转发职责从 Vite 交给 Nginx 来做。在 Nginx 配置里加一段 locationlocation /api/ { proxy_pass http://127.0.0.1:8080; }三种方案可以在同一个项目里组合使用开发走 Vite 代理生产走 Nginx 代理后端保持不提供跨域配置。这样后端的接口更为独立也方便直接供第三方系统调用。4. 前后端联调从零到一打通全流程4.1 联调前的三个准备工作前后端联调不是拉个群两边把代码一跑就算完的。我经历过太多次联调现场因为准备不足而浪费一两天时间的情况。做足准备联调效率能提升一倍。第一个准备是接口文档先行。不要后端写完接口再补文档要在动手写代码之前就把接口的路径、请求参数、返回结构定义清楚。工具上我推荐 Apifox 或者 YApi可以在线管理接口文档并生成 Mock 数据。前端根据文档写页面后端根据文档开发接口两边并行推进联调的时候只需要对细节而不是争论字段叫什么名字。第二个准备是统一错误码规范。项目里返回结构统一是code、message、data但 code 的取值范围和含义要提前约定。比如 200 是成功401 是未认证403 是权限不足500 是服务器内部错误。前端拦截器根据 code 做统一处理比如遇到 401 就跳转登录页遇到 500 就弹出错误提示。如果每个接口返回的错误码风格不同前端拦截器就形同虚设。第三个准备是接口字段命名约束。Java 后端习惯用驼峰命名userName数据库习惯用下划线user_name而前端 JavaScript 两种风格都有人写。我建议前后端接口传输的统一用驼峰命名数据库字段映射交给 MyBatis-Plus 的下划线转驼峰配置前端永远只面对驼峰字段这样最省心。4.2 如何快速区分前后端 Bug联调阶段每天都要面对各种报错如何快速判断问题出在前端还是后端这是每个开发都必须掌握的技能。我有几个高效的定位手段。第一招是看 Network 面板。按 F12 打开浏览器开发者工具切到 Network 标签页找到对应的接口请求。如果请求状态码是 404 或 405说明路径或请求方法不对大概率是后端问题如果状态码是 200但返回数据里的 code 是 500说明后端内部抛异常了点开 Response 看具体错误信息如果请求根本没有发出或者请求 vender 里标着(failed) net::ERR_FAILED那是前端的问题先检查代理配置和接口地址。第二招是分阶段确认。前端能显示的页面结构、交互逻辑属于前端问题数据是否正确、接口是否返回符合预期的内容属于接口问题接口返回的内容是否被正确处理属于前端问题。很多人会在这三层之间来回横跳我的习惯是先在 Network 面板里确认后端返回的原始 JSON 数据是否符合预期如果正确那问题一定在前端处理数据的代码上从那儿往下查很快。第三招是巧用后端日志。SpringBoot 默认会输出 WEB 层的访问日志可以看到每个请求的路径、参数、处理耗时以及异常堆栈。如果前端说接口报错但后端日志里压根没有相关请求记录那多半是请求没到后端问题出在代理或路由配置上。这个判断方法在排查跨域和代理问题时尤其高效。4.3 文件上传下载这类耗时接口的联调细节这个项目里我还加了一个文件上传下载的接口。初看很简单但实际联调时有不少细节容易出问题。前端的文件上传我用 Element Plus 的el-upload组件配置好 action 地址和请求头即可。但有个坑是文件上传接口一般不需要也不应该携带认证 header而我在 Axios 请求拦截器里统一加了Authorization头这会导致部分浏览器触发一次额外的 OPTIONS 预检请求。后端如果没处理好预检请求就会报跨域错误。解决办法是在后端过滤器里对 OPTIONS 请求直接放行if (OPTIONS.equals(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); return; }大文件上传则是另一个话题。如果文件超过 100MB直接上传很容易超时我推荐做分片上传。前端把文件切割成固定大小的分片比如每片 5MB逐个上传后端接收后合并。断点续传也依赖这个基础——记录已上传的分片列表下次上传时跳过已完成的分片。我在项目里实现了一个简单的分片上传接口前端用Blob.prototype.slice切分文件后端收到分片后暂存在临时目录最后一片上传成功后通知后端合并。代码本身不复杂但能覆盖绝大多数大文件上传的业务需求。4.4 Vite 代理配置技巧与常见联调报错汇总前面已经提过 Vite 代理的基础配置这里补充一些实际使用中的进阶技巧。如果你需要同时访问多个后端服务可以通过配置多个代理规则实现proxy: { /api: { target: http://localhost:8080, changeOrigin: true }, /file: { target: http://localhost:9090, changeOrigin: true } }changeOrigin: true这个选项的作用是修改请求头中的 Host 字段为目标地址的 Host如果不设置有些后端服务器会校验 Host 并拒绝请求。联调中最常见的报错我整理成了一张表方便对照排查现象可能原因解决办法浏览器报 CORS error后端未配置CORS或代理规则不匹配检查代理配置或在后端添加CORS配置请求404前端代理路径与后端接口路径不一致核对接口URL前缀请求返回500后端代码异常查看后端日志定位异常堆栈前端拿不到data字段后端返回结构不统一约定统一ResultVO结构Token失效但登录页不跳转拦截器未处理401状态码在响应拦截器中加错误跳转处理4.5 调试工具与接口测试的实用技巧联调阶段有两类工具是效率神器。第一类是前端 Vue 调试工具。Vue 3 对应的浏览器插件是Vue.js devtools在 Chrome 商店就能安装。它能直观查看组件的 props、data、computed 值还能追踪事件和路由。排查响应式数据没更新、computed 计算错误这类问题非常高效。调试的时候先在 devtools 里检查组件 data 的值是否正确如果不正确再看是不是接口数据的问题逐层排查比瞎猜有用得多。第二类是接口测试工具。Apifox 有个很好用的功能——从一个接口的测试用例里直接生成前后端联调时的 Mock 数据。前端页面开发阶段不需要等待后端接口就绪直接调用 Mock 数据就能正常渲染后端接口真正写好后只要把测试环境地址切换到真实后端页面就能无缝衔接真实数据。这个工作流我用了很久确实能有效减少联调等待时间。5. 前后端项目的部署与上线5.1 前端打包常见错误与 Nginx 配置指南前端开发完后的部署我一般分四步打包、上传、配 Nginx、验证。打包前建议确认环境变量配置。Vite 默认区分开发环境和生产环境通过.env.development和.env.production文件管理。生产环境里的VITE_API_BASE_URL我习惯设为空字符串让所有请求都走同源相对路径/api然后交给 Nginx 做代理转发。这样部署路径灵活不需要根据不同机房改前端代码。执行npm run build之后产物在dist目录。上传到服务器后Nginx 配置的核心部分长这样server { listen 80; server_name your-domain.com; root /var/www/frontend/dist; index index.html; # 前端路由 history 模式刷新支持 location / { try_files $uri $uri/ /index.html; } # 接口反向代理 location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }try_files $uri $uri/ /index.html;这一行极其关键。如果没有这一行刷新http://your-domain.com/user页面时Nginx 会去物理目录找 user 文件找不到就报 404。加上之后Nginx 发现文件不存在会回退到index.html由前端路由接管渲染。这个坑我在初学阶段栽过一次后来每次部署都要确认一遍。5.2 使用 Docker Compose 一键编排前后端服务如果服务器是全新的我推荐直接用 Docker Compose 来编排服务。把 MySQL、后端、前端 Nginx 打成三个容器一条命令启动所有服务可复现性和可维护性都很好。先说镜像构建。前端需要一个带 Nginx 的自定义镜像Dockerfile 大概长这样FROM node:18-alpine AS build WORKDIR /app COPY package*.json ./ RUN npm install --registryhttps://registry.npmmirror.com COPY . . RUN npm run build FROM nginx:alpine COPY --frombuild /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD [nginx, -g, daemon off;]这里用了多阶段构建——第一阶段用 Node 镜像编译前端资源第二阶段把编译产物拷贝进干净的 Nginx 镜像。这样最终镜像体积小也不包含编译工具链的冗余文件。后端同理FROM maven:3.8-openjdk-8 AS build WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn package -DskipTests FROM openjdk:8-jre-alpine COPY --frombuild /app/target/demo.jar /app/demo.jar EXPOSE 8080 ENTRYPOINT [java, -jar, /app/demo.jar]然后写docker-compose.ymlversion: 3.8 services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: demo_db ports: - 3306:3306 volumes: - mysql_data:/var/lib/mysql restart: always backend: build: ./backend depends_on: - mysql environment: SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/demo_db?useSSLfalseserverTimezoneAsia/Shanghai SPRING_DATASOURCE_USERNAME: root SPRING_DATASOURCE_PASSWORD: root123 ports: - 8080:8080 restart: always frontend: build: ./frontend depends_on: - backend ports: - 80:80 restart: always volumes: mysql_data:这里要特别注意的是 Docker Compose 内部网络的 DNS 解析——后端容器里访问数据库不能写localhost而要写服务名mysql。同理前端 Nginx 容器里反向代理后端也不应该写127.0.0.1而要写后端服务名backend。这个细节如果忽略部署上去就会发现容器之间网络不通。启动命令很简单docker-compose up -d --build整个前后端项目在三分钟内就能在任意一台安装好 Docker 的服务器上跑起来。这对于交付演示环境、临时测试环境来说非常省事。5.3 云服务器部署的完整步骤与常见坑不习惯用 Docker 的话在云服务器上直接部署也更常规。流程一般是服务器装好 JDK 和 Nginx上传后端 jar 包运行再上传前端 dist 目录到 Nginx 站点目录。后端 jar 包启动推荐用nohup加日志输出nohup java -jar demo.jar --spring.profiles.activeprod app.log 21 这里有两个注意点。第一--spring.profiles.activeprod指定配置文件生产环境走生产环境的数据库配置。第二 app.log 21将标准输出和错误输出都重定向到日志文件这样排查问题的时候可以在日志文件里看到完整的异常信息。启动后验证接口是否正常curl http://127.0.0.1:8080/api/auth/login如果后端正常再检查 Nginx 配置是否生效nginx -t nginx -s reload最后从浏览器访问http://服务器公网IP能看到前端页面说明部署成功。云服务器部署常见的坑我来盘一盘第一安全组端口没开。阿里云和腾讯云的服务器默认只有 22 端口是开放的要在控制台安全组里放行 80 和 8080 端口否则外部无法访问。这个问题非常常见我曾经在一次项目演示前五分钟才发现端口没开当场排查了半天。第二数据库访问权限问题。云服务器的 MySQL 默认只监听本地回环地址后端在与数据库连接时要注意账号是否有远程访问权限但在单机部署中通常都用localhost连接这时问题不大。如果数据库单独部署在另一台机器上就要到 MySQL 用户权限表里给应用账号授权。第三防火墙冲突。云服务器上可能同时存在云安全组和系统内部的 firewalld/iptables 两层防护两层都要放行对应端口。经常有人配好了安全组仍然访问不了一查发现是机器内防火墙把端口拒了。6. 常见问题与排查技巧实录6.1 Maven 与 npm 依赖下载超时的终极解决方案几乎每个新人都被这个问题折磨过。npm 依赖装一半卡住不动Maven 依赖下载一直报超时本质原因都是默认源在国外网络不稳定。Maven 源修改很简单打开 Maven 安装目录下的conf/settings.xml在mirrors节点里加入阿里云镜像mirror idaliyunmaven/id mirrorOfcentral/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirrornpm 源前面已经说过改成registry.npmmirror.com就行。这俩改完之后下载速度提升一个量级基本上不会再有超时问题。还有一种情况本地网络本身没问题但依赖就是下载慢。可以试试先手动下载 jar 包放到本地仓库里再用mvn install:install-file命令安装到本地。这种方式在应急场景下比较有用比如某一天某个依赖分发出问题了从中央仓库拿不到包。6.2 SpringBoot 版本太高导致的各种兼容性问题版本选型是后端项目成败的关键细节之一。我见过不少项目因为用了刚发布的最新版 SpringBoot结果引入某个第三方库时发现对方还没有适配新版本导致项目启动失败或者运行时出现古怪异常。SpringBoot 3.x 最大的变化是底层从javax.*切换到了jakarta.*命名空间这意味着所有依赖这个命名空间的第三方库都需要升级适配。如果你在项目中还在用一些老牌的、维护不频繁的库某些公司内部的框架、老版本的工作流引擎升级到 SpringBoot 3.x 基本就是劝退。我的经验是公司项目跟着稳定版本走个人学习可以跟着最新版本走。目前 2.7 和 3.2 都是相对稳妥的选择。如果项目里有大量历史遗留依赖2.7 会更合适如果是全新项目直接 3.2 也没问题毕竟技术总要往前迭代。6.3 Vue 常见面试考点与项目实战的结合这个项目写完之后最大的收益其实是面试。面试官问 Vue 相关的问题基本都绕不开这几个v-if和v-show的区别、Vue 3 组合式 API 和选项式 API 的区别、key的作用、路由守卫的用法。如果只是背概念答不出亮点但如果结合项目实战来讲效果会好很多。比如v-if和v-show——实际项目中登录状态、权限按钮这种频繁切换的场景我一般用v-show因为它只是切换 CSS 的display属性不需要重新渲染组件开销小而像低概率出现的弹窗、不同角色的大块页面内容我会用v-if因为它能彻底销毁组件避免内存泄漏。再比如路由守卫我的项目里不仅做了登录校验还做了一个小的权限控制根据后端返回的角色信息在路由守卫里判断用户是否有权访问某个页面没有权限就跳转 403 页面。把这个逻辑讲出来面试官会觉得你是真的在项目里解决过问题而不是背概念。还有一个必问的Vue 3 组合式 API 和选项式 API 的区别。我在项目里两种都写过实际体会是——组合式 API 对复杂逻辑的复用确实方便用computed、watch、ref这些组合函数能把原本散落在data、methods、watch里的代码组织到一起。但简单页面用选项式 API 也完全没问题代码反而更直观。面试官问这个问题的时候你就可以说两种都实践过我的选型标准是逻辑复杂度而不是盲目追新。7. 项目扩展方向与我的实操心得项目跑通之后完全可以基于这个骨架继续扩展。我列几个我实际做过或者觉得很有价值的方向。第一个方向是引入工作流引擎。SpringBoot 集成 Flowable 是做审批类系统最常见的需求。比如请假审批、费用报销这些场景用 Flowable 定义好流程模板后端通过 API 启动流程实例在任务完成节点做业务处理前端通过接口查询待办任务、已办任务。这套能力在很多企业级系统里都是刚需。Flowable 7 有相对好用的 REST API可以直接在前端调用也可以包一层自己的接口做权限控制。第二个方向是音视频处理。Vue 前端播放 HLS 视频流m3u8 格式的需求越来越多特别是直播回放、监控视频这类场景。前端可以用hls.js这个库几行代码就能实现 m3u8 的播放。需要注意的是浏览器原生的 video 标签并不支持 m3u8 格式除了 Safari必须通过hls.js做转封装或者使用带 MSE 支持的播放器组件。这个功能我建议在有相关业务需求时再引入不要一开始就把项目的复杂度抬高。第三个方向是系统集成能力。项目里可以接入第三方平台的开放能力。前端做网页版 H5 应用时经常需要对接 IM 工具和内部平台的免登录流程需要遵循对应的 OAuth 授权协议。开发方式大同小异拿到授权码之后请求后端交换 token后端记录用户信息并生成自己的登录态。整个项目从搭建到部署走下来我最大的体会是前后端分离项目真正的难点不在技术栈本身而在工程化意识。环境怎么统一、接口怎么约定、错误怎么排查、部署怎么做这些能力比单纯会写几个组件和接口重要得多。希望这篇内容能帮你把这条链路完整地走一遍后续不管换什么框架、换什么语言底层的思路都是相通的。最后分享一个我这些年一直在用的习惯每次新项目动工前先花半小时把接口文档和目录结构定好本地能少熬两天的夜。这个投入产出比是我踩过无数坑之后最想告诉你的。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价