资讯动态

JavaWeb与Vue前后端分离实战:从环境搭建到部署全攻略

发布时间:2026/10/9 7:23:07 来源:尧图企业网站定制
1. JavaWeb 与 Vue 的前后端协同底层逻辑这几年做 JavaWeb 项目最绕不开的一个话题就是前后端分离。我刚接触这套技术栈的时候也是一股脑把 Java 后端代码和 Vue 前端代码塞在同一个项目里用 idea 直接启动一个 Tomcat然后前端页面靠 JSP 渲染。那时候确实很方便但随着业务复杂起来JSP 的维护成本越来越高动不动就是一整个页面堆积了大量 Java 代码逻辑和 HTML 标签改一个样式都要小心翼翼生怕动了某个隐藏的 Java 代码块。后来我转向前后端分离架构后端用 Spring Boot 提供纯接口前端用 Vue 构建独立应用。这个转变带来最直观的变化就是职责清晰Java 只负责数据接口、事务处理和权限控制Vue 只负责页面交互、路由跳转和状态管理。这样两边可以并行开发前端不需要等后端把页面模板写好后端也不需要关心某个按钮到底长什么样。不过前后端分离不是简单地把代码拆开就行真正的难点在于双方如何约定数据结构、如何处理跨域、如何保证联调效率。我见过不少团队前后端分是分了但接口文档全靠口头传字段名改来改去前端拿到的时间字段是一串数字时间戳后端又期望前端传一个固定格式的字符串联调阶段简直是一场灾难。这里我给大家一个很实在的建议无论项目大小先把接口规范定下来。拿一个用户管理模块来说后端返回的数据格式可以统一设计成下面这样。{ code: 200, message: 操作成功, data: { userId: 1, username: zhangsan, createTime: 2025-01-12 10:30:00 } }code 用来标识业务状态200 是成功500 是服务器异常401 是未登录403 是无权限。message 用来展示给用户看的提示信息。data 才是真正的业务数据。这个结构虽然简单但能让前后端对接时的沟通成本降到最低。时间字段这个问题也应该引起重视。Java 后端默认返回的时间可能是2025-01-12T10:30:00也可能是时间戳Vue 这边如果不做处理直接展示用户看到的就是一串数字。我的习惯是后端统一返回yyyy-MM-dd HH:mm:ss格式化后的字符串前端拿到直接渲染就行。只要约定好了同类问题就不会反复出现。跨域问题也是前后端分离绕不开的坎。我在 idea 里启动后端项目端口是 8080Vue 开发服务器跑在 5173前端页面去请求后端接口浏览器会直接拦截因为端口不同就属于跨域。解决方案有很多我偏向于在后端写一个 CORS 全局配置类。Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }这个配置类的作用就是把后端所有接口都放开跨域限制。开发阶段这样配没问题但到了生产环境建议把 allowedOriginPatterns 改成具体的域名不然谁都能跨域请求你的接口安全上会存在隐患。前后端分离的核心不是技术选型而是协作方式。Java 后端和 Vue 前端各司其职又通过统一的接口约定紧密协作这才是这套技术栈真正高效运转的关键。理解了这层关系后面不管是配置环境、编写代码还是打包部署思路都会顺畅很多。2. 从零搭建 JavaWeb 与 Vue 的开发环境很多新手在环境配置这一关就被卡住了网上的教程东拼西凑不是版本对不上就是步骤缺胳膊少腿。我自己一路踩坑过来把关键环境配置整理成一套可以直接照做的流程不说百分之百覆盖所有情况但至少能帮你少走大半弯路。2.1 idea 运行 JavaWeb 项目配置要点idea 里跑 JavaWeb 项目最常见的组合是 JDK 8、Maven 3.6、Spring Boot 2.x。这三个版本搭配起来最稳网上的资料也最多遇到问题随便一搜就能找到解决方案。JDK 8 虽然年头久了但在企业级项目里依然占据主流很多老系统的运行环境就是它。先检查 Maven 配置。idea 自带的 Maven 版本不一定合适我建议用自己下载的 Maven并且把 settings.xml 里的本地仓库路径改成一个容易找到的位置。具体操作是打开 idea 的设置搜索 Maven分别设置 Maven home path、User settings file 和 Local repository。很多人忽略了这一步结果 Maven 依赖下载到了 C 盘的系统目录里越积越多最后磁盘空间告急。Maven 依赖下载慢的问题也很常见。国内访问中央仓库经常超时我的解决办法是在 settings.xml 里配置阿里云的镜像。这个操作属于常规开发实践能极大提升依赖下载速度减少等待时间。mirror idaliyunmaven/id mirrorOfcentral/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror配好镜像后新建 Spring Boot 项目时idea 会自动从镜像仓库拉取依赖速度比之前快了好几倍。另外建议给 idea 的 Maven 设置里勾选上自动导入依赖这样 pom.xml 一改动依赖就会自动更新省去手动刷新的麻烦。运行配置也不难。Spring Boot 项目本质上是一个带 main 方法的 Java 应用直接在 idea 里运行 application 类就行。如果你想用 Tomcat 来跑也不是不可以但建议用 Spring Boot 内置的 Tomcat毕竟把项目打包成 jar 直接java -jar运行比部署 war 包要简单得多。2.2 Vue 安装及环境配置保姆级流程Vue 的环境配置看着麻烦其实核心就三样Node.js、npm 和 Vue CLI。Node.js 是 JavaScript 的运行环境npm 是包管理工具Vue CLI 是脚手架工具。装好这三样一个 Vue 项目就能跑起来了。Node.js 的版本要注意一下Vue CLI 5.x 要求 Node.js 版本不低于 12但也不要太高。我遇到过 Node.js 版本过新导致某些依赖编译报错的情况目前用 Node.js 16 LTS 版本最稳兼容性最好。去官网下载对应安装包一路下一步就行没什么特别的操作。装完 Node.js 后npm 也跟着装好了。不过 npm 默认的下载源在国外安装依赖时经常卡住和 Maven 一样我们需要配置一下镜像源。npm config set registry https://registry.npmmirror.com这个命令执行完后可以输入npm config get registry验证一下只要能打印出刚才设置的地址就说明镜像源配置成功了。接着安装 Vue CLI。npm install -g vue/cli全局安装完成后输入vue --version检查版本。看到版本号输出说明 Vue CLI 已经就绪。接下来创建一个项目。vue create my-project创建过程中会让你选择预设配置新手建议直接选 Default也就是 Vue 3 Babel ESLint 的基本组合。项目创建完成后进入项目目录执行npm run serve浏览器打开终端提示的地址就能看到 Vue 的欢迎页面了。到这里Vue 的基础环境就算搭建完毕。后面你就可以在这个项目里编写组件、配置路由、引入 UI 库一步步搭建自己的前端应用。2.3 环境配置常见坑位排雷环境配置这块我踩过不少坑挑几个最典型的分享一下。第一个坑是 JDK 版本不匹配。Spring Boot 2.x 依赖的某些库在 JDK 17 上会报错而 Spring Boot 3.x 又强制要求 JDK 17 及以上。如果你的项目是照着老教程做的用了 Spring Boot 2.7却装了 JDK 17启动时大概率会碰到UnsupportedClassVersionError。解决办法很简单把 JDK 切换回 8或者在 pom.xml 里升级 Spring Boot 版本并同步调整依赖。第二个坑是 Node.js 与依赖版本冲突。创建 Vue 项目时如果遇到Node.js version 12.0.0 is not supported这类提示一方面要检查 Node.js 版本是否过低另一方面也可能是依赖库版本过新对 Node 版本要求高。我习惯把 Node.js 锁定在 LTS 版本能省掉很多麻烦。第三个坑是端口冲突。后端接口占用了 8080Vue 开发服务器占用了 5173但当两个项目同时运行时如果其中某个端口被其他进程抢占启动就会失败。Windows 下可以用下面这个命令查看端口占用情况。netstat -ano | findstr 8080找到占用端口的进程 PID 后在任务管理器里结束对应进程或者修改项目的端口配置。后端是在 application.properties 里修改 server.port前端是在 vite.config.js 里修改 server.port。环境配置其实就是一层窗户纸捅破了就没什么神秘的。按照上面的流程走一遍正常情况下半小时内就能把 JavaWeb 后端和 Vue 前端的环境都跑通。3. Vue 核心知识点实战拆解Vue 的学习曲线相比其他前端框架算是比较平滑的但有一些核心知识点是必须吃透的。这些知识点不搞明白写出来的代码可能也能跑但一旦遇到复杂场景就会手忙脚乱。我把 Vue 开发中最高频的几个知识点拿出来逐一拆解。3.1 vue 指令简写与样式冲突处理Vue 指令是模板渲染的基石。最常用的 v-bind 和 v-on 都有简写形式v-bind 简写成冒号v-on 简写成 。刚开始写的时候可能不习惯但用顺手了会发现代码简洁很多。!-- 完整写法 -- img v-bind:srcimageUrl v-on:clickhandleClick !-- 简写写法 -- img :srcimageUrl clickhandleClickv-model 用于表单双向绑定v-if 和 v-show 用于条件渲染v-for 用于列表渲染v-html 用于渲染 HTML 字符串。这里提醒一下项目实战中的安全意识尽量不要用 v-html 渲染用户输入的内容很容易引发 XSS 攻击。如果非用不可也要在后端做好数据清洗和过滤。样式冲突是组件化开发中绕不开的话题。默认情况下Vue 组件的样式是全局生效的也就是说 A 组件里定义的类名在 B 组件里也能用这就可能导致样式互相覆盖。解决办法是在 style 标签上加上 scoped 属性。template div classcard p文章内容/p /div /template style scoped .card { border: 1px solid #eee; padding: 16px; } /stylescoped 的原理是给当前组件的元素添加一个特殊的 data 属性然后通过属性选择器来限定样式作用域。加了 scoped 后组件内部的样式就不会污染到外部外部的样式也影响不到组件内部模块化效果非常好。如果你在项目里发现明明加了 scoped子组件里的样式还是被改了那通常是因为你用到了子组件的根元素或者用了第三方组件的内部类。Vue 的 scoped 样式对子组件根元素是生效的但子组件内部的 DOM 结构样式就不会渗透进去了。这种情况下要么给子组件也加上和父组件相同的类名要么用 :deep() 深度选择器来穿透作用域。style scoped .parent :deep(.child-inner) { color: red; } /style:deep() 在 Vue 3 里就是这么写的Vue 2 里对应的是 ::v-deep。遇到需要覆盖第三方组件内部样式的情况这个选择器特别有用。3.2 vue 插槽机制与组件复用插槽是 Vue 组件化开发中非常强大的功能。它解决的问题是组件的外部结构和内部逻辑如何灵活组合。最简单的情况是匿名插槽。我在做一个卡片组件卡片的结构是固定的但卡片内容是不同的。这时候卡片组件可以写成这样。template div classcard div classcard-header{{ title }}/div div classcard-body slot/slot /div /div /template在使用卡片组件的地方组件标签中间的内容就会被填充到 slot 的位置。MyCard title用户信息 p用户名张三/p p年龄25/p /MyCard有时候一个组件需要多个插槽位置比如页头、主体、页脚。这时候就需要给插槽命名也就是具名插槽。template div classlayout header slot nameheader/slot /header main slot/slot /main footer slot namefooter/slot /footer /div /template使用的时候用 template 标签搭配 v-slot 来指定插槽名称。MyLayout template v-slot:header h1页面标题/h1 /template p主体内容/p template v-slot:footer p版权信息/p /template /MyLayout具名插槽还有一种简写方式用 # 替代 v-slot。上面的写法可以简写成这样。MyLayout template #header h1页面标题/h1 /template p主体内容/p template #footer p版权信息/p /template /MyLayout还有一种作用域插槽它的作用是让父组件能够访问子组件的数据。比如表格组件把当前行的数据通过插槽传递出来让父组件可以决定每行如何渲染。这种场景在做数据表格的时候特别常见尤其是需要自定义操作列的时候。3.3 vue 路由与动态路由参数传递Vue 路由是单页应用的核心。没有路由就没法实现多页面的跳转体验。路由配置看似简单但真正写起来有不少细节需要注意。基础路由配置是定义一个路由数组然后调用 createRouter 创建路由实例。import { createRouter, createWebHistory } from vue-router const routes [ { path: /, component: Home }, { path: /user/:id, component: UserDetail } ] const router createRouter({ history: createWebHistory(), routes })这里注意/user/:id这种写法冒号表示 id 是动态参数。当用户在地址栏输入/user/1001时路由就能匹配到 UserDetail 组件并且组件里可以通过route.params.id获取到 1001 这个值。路由参数传递有两种方式一种是路径参数另一种是查询参数。路径参数是上面的写法查询参数是地址栏里常见的?id1001这种形式。!-- 路径参数 -- router-link to/user/1001用户详情/router-link !-- 查询参数 -- router-link to/user?id1001用户详情/router-link在组件里分别通过route.params和route.query来获取。动态路由还有一个高级应用就是根据用户权限生成路由表。在做后台管理系统的时候不同角色的用户能访问的页面不一样。管理员能看到用户管理、权限设置普通员工只能看到自己的工单列表。这时候不能把所有路由都配置在前端而是等用户登录后根据后端返回的权限列表动态添加路由。router.addRoute({ path: /admin, component: AdminLayout, children: [ { path: users, component: UserManage } ] })addRoute 可以随时动态添加路由记录。这个功能在做权限控制时非常关键可以避免用户通过修改路由直接越权访问页面。当然真正的安全控制不能只靠前端路由后端的接口也要做权限校验前端路由隐藏只是用户体验层面的优化。另外提醒一下路由守卫。在跳转前判断用户是否登录、是否有权限都应该在全局前置守卫里做。router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.path /login) { next() } else { if (!token) { next(/login) } else { next() } } })这段逻辑虽然简单但非常实用。未登录用户访问任何受保护页面都会自动跳到登录页。3.4 vue 打包放进 Spring Boot 中的完整流程项目开发完成后前端代码最终要部署到服务器上。最常规的做法有两种一种是前端单独部署到 Nginx通过反向代理转发请求到后端另一种是把 Vue 打包后的静态资源放进 Spring Boot 项目中让后端统一托管。很多前辈更喜欢后一种方式因为部署时只需要动一个进程管理起来更省事。打包 Vue 项目需要先执行构建命令。npm run build构建完成后项目根目录下会生成一个 dist 文件夹里面是打包后的静态文件。接下来要做两件事一是把 dist 文件夹里的内容复制到 Spring Boot 项目的 src/main/resources/static 目录下二是确保后端对前端路由的支持。这里有个关键问题Vue Router 使用的是 HTML5 History 模式路径如/user/1001是真实的浏览器地址。当用户直接在浏览器地址栏输入这个地址访问时后端会去找对应的接口或资源如果找不到就会返回 404。解决办法是在后端配置一个错误页面跳转将所有非接口请求都转发到 index.html。在 Spring Boot 里可以做这样一个简单的控制器实现路径转发。Controller public class ViewController { RequestMapping(value {/, /user/**, /admin/**, /login/**}) public String forward() { return forward:/index.html; } }需要注意的是接口路径不能被转发到 index.html否则接口请求会变成 HTML 页面返回前端拿到一堆字符串无法解析。所以要确保这个控制器只匹配前端路由路径不要覆盖后端业务接口的路径。还有一种方案是把 Vue Router 切换成 hash 模式。hash 模式的路径是/#/user/1001这种形式不会触发后端路由因为#后面的内容浏览器不会发送给服务器。所以如果不想配置后端转发直接使用 hash 模式最简单但地址栏会多一个#不太美观。在踩过几次坑之后我的习惯是不推荐用 hash 模式因为历史记录和分享链接的体验不如 History 模式。体验需要的话还是配一个后端转发比较好。打包放进 Spring Boot 的方法还有一个好处就是前后端同源部署不再有跨域问题。开发环境要靠 CORS 配置来解决跨域生产环境部署在一起后反而更简单。4. 高价值 Vue 进阶场景与前端面试精华在 JavaWeb 和 Vue 的结合使用中有一些进阶场景非常高频也是面试官重点考察的方向。我把这些内容整理一下无论是为了做项目还是准备面试都能用得上。4.1 vue 中使用 ECharts 绘制统计图ECharts 是数据可视化领域用得最多的图表库之一。在后台管理系统中销售统计、用户增长、流量趋势这些都是 ECharts 的强项。用 Vue 集成 ECharts 并不复杂不少新手的困惑是不知道数据如何从后端传递到图表中。先安装依赖。npm install echarts然后在需要的组件中引入。template div refchartRef stylewidth: 600px; height: 400px/div /template script setup import { ref, onMounted, onBeforeUnmount } from vue import * as echarts from echarts const chartRef ref(null) let chartInstance null onMounted(() { // 初始化图表实例 chartInstance echarts.init(chartRef.value) // 模拟从后端获取的数据 const months [一月, 二月, 三月, 四月, 五月, 六月] const salesData [120, 200, 150, 180, 240, 300] chartInstance.setOption({ title: { text: 月度销售统计 }, tooltip: {}, xAxis: { data: months }, yAxis: {}, series: [ { name: 销售额, type: bar, data: salesData } ] }) }) onBeforeUnmount(() { // 组件销毁前注销实例避免内存泄漏 if (chartInstance) { chartInstance.dispose() } }) /script在真实项目里上面的模拟数据会被替换成 axios 请求后端接口返回的数据。而且我觉得第一次加载完数据之后可以做一个窗口大小变化的监听当浏览器窗口变化时自动调整图表尺寸这个细节很多教程里都不会告诉但实际项目中缺了它图表就会变形。真正要画两个柱状统计图进行对比的场景里核心在于 series 数组里放两个数据源每个数据源都有独立的颜色和 name然后结合 legend 图例组件展示。这种设计在做同比环比对比时特别常用。4.2 vue 播放 m3u8 格式视频的免安装方案m3u8 是 HLS 流媒体协议中的索引文件格式现在很多视频网站都在用。在 Vue 项目中播放 m3u8 格式的视频最主流的方案是使用 hls.js 这个库。先安装依赖。npm install hls.js然后封装一个播放组件。template video refvideoRef controls autoplay muted/video /template script setup import { ref, onMounted } from vue import Hls from hls.js const videoRef ref(null) onMounted(() { const video videoRef.value const videoUrl https://example.com/live/stream.m3u8 if (Hls.isSupported()) { const hls new Hls() hls.loadSource(videoUrl) hls.attachMedia(video) hls.on(Hls.Events.MANIFEST_PARSED, () { video.play() }) } else if (video.canPlayType(application/vnd.apple.mpegurl)) { // 原生支持 HLS 的浏览器如 Safari video.src videoUrl } }) /script这个方案不需要安装任何浏览器插件只要浏览器支持 HTML5 video 标签就能播放 m3u8 格式的视频流。在直播、点播、课程平台、监控回放等场景里非常常见。hls.js 的优势是兼容性极好各主流浏览器都能用。在处理视频流的过程中它内部会自动处理网络波动、分段切换等逻辑。不过有一点要注意播放的 m3u8 地址必须和后端接口在同一域名下或者后端配置了正确的跨域响应头否则视频请求会被浏览器拦截。4.3 vue 低代码平台与拖拽组件生成页面低代码平台这几年热度很高。它的核心玩法是拖拽组件到画布上自动生成前端页面代码。在 Vue 技术栈里实现拖拽生成页面主要依靠 HTML5 的拖放 API 或者第三方拖拽库。我推荐使用 vuedraggable 这个库它基于 Sortable.js用起来比较顺手。npm install vuedraggable基本用法是封装一个可拖拽的组件列表和一个可放置的页面画布。template div classeditor-container div classcomponent-palette draggable v-modelcomponentList :group{ name: components } div v-foritem in componentList :keyitem.type classcomponent-item {{ item.label }} /div /draggable /div div classcanvas draggable v-modelpageComponents :group{ name: components } div v-foritem in pageComponents :keyitem.id classcanvas-item component :isitem.type v-binditem.props/component /div /draggable /div /div /template整体思路是维护两个数组一个存放可选组件库另一个存放画布上已放置的组件。通过 group 属性让两个区域共享同一个拖拽组这样组件就能从组件库拖到画布上。被拖入画布后根据组件的 type 动态渲染对应的 Vue 组件用户还可以在属性面板中修改组件的 props实时预览效果。拖拽组件的原理理解清楚后再进阶就是为每个组件保存位置信息 x、y、宽高。这样最终可以生成一段 JSON 配置再用自定义渲染器把 JSON 还原成页面。这种设计思路已经接近很多低代码平台的核心原理了自己做一个小型低代码引擎也不是不可能。4.4 vue 前端面试高频题与答题思路结合做 JavaWeb 项目时的实际场景我整理几个面试中出现频率较高的 Vue 题目和自己总结的答题思路。第一个题是 Vue 的生命周期。这个属于必考题。我的回答思路是把创建阶段、挂载阶段、更新阶段、销毁阶段各方法的作用讲清楚然后结合一个实际场景说明。比如在 onMounted 里去请求列表数据在 onBeforeUnmount 里清理定时器或注销事件监听。这样比干巴巴背概念更有说服力。第二个题是 computed 和 watch 的区别。我的回答思路是 computed 依赖响应式数据数据没变不会重新计算适合做派生状态比如购物车总价。watch 是监听数据变化后执行副作用操作适合做搜索关键词防抖。两者的本质区别是一个是声明式派生一个是命令式监听。第三个题是 v-if 和 v-show 的区别。v-if 是真正的条件渲染满足条件才创建 DOM不满足就销毁适合初始化渲染频率低的场景。v-show 是用 CSS 的 display 属性控制显隐元素始终在 DOM 中适合需要频繁切换的场景。如果在后台管理系统做菜单权限控制v-if 更合适做 Tab 页签切换v-show 更合适。第四个题是组件通信方式。父传子用 props子传父用 emit 事件。跨层级通信可以用 provide/inject也可以用 Pinia 这种状态管理库。兄弟组件之间的通信一般通过公共父组件或者 Pinia 来完成。答题时尽量结合自己做过的模块来描述比如做了购物车那购物车数据共享就是一个很好的例子。第五个题是 Vue 3 和 Vue 2 的核心区别。组合式 API 和选项式 API 是最大的区别Vue 3 的 Composition API 能把同一逻辑的代码聚在一起。其次是响应式原理从 Object.defineProperty 换成了 Proxy这让 Vue 3 能监听新增属性和删除属性这是 Vue 2 做不到的。还有 Fragment、Teleport、Suspense 这些新特性都是加分项。当然面试不只是背题更重要的是展示你的项目经验和思考能力。如果你能讲清楚为什么这样做、遇到问题怎么排查那比单纯回答知识点更能加分。5. JavaWeb 完整项目实战复盘与后续扩展前面把理论讲了不少这一部分我结合一个仿天猫购物系统的用户管理模块来实战复盘。这是我比较推荐的学习路径不要上来就做一个完整的电商系统那样容易陷入代码量过大、无从下手的困境。先做一个模块把 JavaWeb 和 Vue 的协作链路完全跑通再逐步扩展。5.1 项目整体设计与数据库建模这个模块的功能包含注册、登录、个人信息查看与修改、用户列表管理。虽然是简化版但麻雀虽小五脏俱全涉及了后端接口设计、数据库表设计、前端页面编写、权限控制等多个核心环节。数据库表设计是整个项目的根基。我会把用户表设计成下面这个样子。CREATE TABLE t_user ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, username varchar(50) NOT NULL COMMENT 用户名, password varchar(100) NOT NULL COMMENT 密码加密存储, nickname varchar(50) DEFAULT NULL COMMENT 昵称, email varchar(100) DEFAULT NULL COMMENT 邮箱, phone varchar(20) DEFAULT NULL COMMENT 手机号, status tinyint(4) DEFAULT 1 COMMENT 状态1正常0禁用, create_time datetime DEFAULT NULL COMMENT 创建时间, update_time datetime DEFAULT NULL COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8mb4;密码一定不能明文存储我用的是 BCrypt 加密。Spring Security 里有现成的 BCryptPasswordEncoder直接拿过来用就行。即使是自己实现注册逻辑也建议引入这个工具类而不是搞简单的 MD5 加盐。BCrypt 会自动加盐而且每次加密结果不同安全性会高很多。项目结构上按照 Spring Boot 的标准分包方式controller、service、mapper、entity 四层分离。每一层只做好自己那一层的事controller 接收前端请求并校验基础参数service 处理业务逻辑与事务mapper 负责数据库交互entity 对应数据表字段。5.2 后端接口开发与接口规范落地注册接口的核心逻辑是检查用户名是否重复然后对密码加密最后写入数据库。这里有个细节需要注意判断用户名是否重复时要考虑并发场景。两个用户同时提交同一个用户名可能都通过了检查然后其中一条插入就会因为数据库唯一索引冲突而报错。我会在 service 层捕获这个异常转成友好的业务提示信息而不是直接让该死的 SQL 异常抛给前端。登录接口稍有不同。前端提交用户名和密码后后端先检查用户名是否存在然后用 BCryptPasswordEncoder 的 matches 方法比对密码。登录成功后向后端返回一个 token。这个 token 可以用 JWT 来生成包含用户 ID、用户名和过期时间。前端拿到 token 后存储在 localStorage 中后续每次请求都带上后端通过拦截器校验 token 合法性。用户列表接口的设计也值得说一说。真实场景里列表请求一般都会带分页参数和搜索关键词。后端接口定义成下面这样。GetMapping(/admin/users) public Result getUsers( RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size, RequestParam(required false) String keyword ) { PageUser userPage userService.pageQuery(page, size, keyword); return Result.success(userPage); }page 代表第几页默认是 1。size 代表每页多少条默认是 10。keyword 是搜索关键词可以为空。service 层配合 MyBatis-Plus 的分页插件可以把分页逻辑写得很简洁。接口开发过程中统一返回结构要坚持使用。我会建立一个 Result 类所有 controller 方法都返回它而不是直接返回一个 User 对象或者 Page 对象。这样前端在处理响应时永远只用关心一个固定的数据格式省去了大量判断逻辑。5.3 前端页面实现与联调技巧前端这边我做的页面有注册页、登录页、个人中心页、管理员用户列表页。用 Vue CLI 创建项目后引入 Element Plus 作为 UI 组件库页面写起来会快很多。先安装 Element Plus。npm install element-plus然后全局引入。import ElementPlus from element-plus import element-plus/dist/index.css import { createApp } from vue import App from ./App.vue const app createApp(App) app.use(ElementPlus) app.mount(#app)登录页的表单校验是前端项目中一个细节较多的部分。用户名必填、密码长度不少于 6 位、邮箱格式要合法。Element Plus 的表单校验规则可以很灵活地配置把这些规则集中管理起来比在方法里逐个判断要整洁得多。axios 请求封装也是必做的一件事。我会创建 request.js统一配置 baseURL、超时时间和请求拦截器。import axios from axios const request axios.create({ baseURL: /api, timeout: 10000 }) 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) { // 统一处理业务错误 return Promise.reject(new Error(res.message)) } return res }, error { // 统一处理 HTTP 错误如 401 跳转登录页 if (error.response.status 401) { router.push(/login) } return Promise.reject(error) } )前端联调时我会强烈建议先通过 jsonmock 或本地 mock 数据把页面渲染跑通再切换成真实接口。这样能把前端问题和后端问题解耦联调时一旦出错定位会更快。jsonmock 这个工具在联调阶段非常好用。它的原理很简单你可以配置一个假的接口地址返回预设的 JSON 数据。前端先用假数据渲染列表、处理分页后面联调时只需要把 baseURL 从 mock 地址切回真实后端地址就行了。具体步骤是用 jsonmock 提供的在线服务配置一个模拟接口比如https://www.fastmock.site/mock/xxx/user/list返回的数据结构设计成和真实接口完全一样。前端代码里只需要在 request.js 中修改环境变量就能实现 mock 和真实数据的无缝切换。这个习惯能彻底打破前后端开发的同步依赖。5.4 项目扩展方向与个人实操心得这个模块完整跑通后向完整项目扩展的方向就比较明确了。比如添加商品模块、订单模块、购物车模块。做这些扩展的时候你会发现 Vue 组件复用价值大幅提升一个商品卡片组件可以在列表页、详情页、搜索页重复使用。Java 后端的分层结构也让添加新功能变得很顺畅新增 controller、service、mapper 三件套就完成一个业务维度。我还想分享个人实操中的体会。第一次独立做 JavaWeb 和 Vue 结合的项目时我犯过不少错其中最大的教训就是没有先设计接口文档直接开写代码后端字段名一改再改前端被迫跟着同步修改浪费了大量时间。后来我在项目开始前先用表格把接口路径、请求方式、请求参数、响应字段全部列清楚开发效率立刻提高了一个档次。从那以后即使是自己一个人做的小项目我也会保持先列接口再写代码的习惯。这样看似多花了一点规划时间但后面开发、联调、测试的速度会快很多。环境问题我也有一个比较实用的心得。开发时把 MySQL 数据库、Redis、后端服务和前端开发服务器都保持运行状态不要把数据库动不动就关掉。很多人一看报错就怀疑代码结果排查半天发现是数据库没启动。我的做法是环境检查永远排在代码排查之前端口通不通、服务启没启、数据库连不连得上这三个问题确认没有问题再去看日志报错。如果你也想做类似的项目我的建议是不要贪大求全。先拿一个模块练手把 JavaWeb 和 Vue 的生态链路彻底走通再逐步扩展。走通一遍之后你就会感觉整套技术栈就像组装积木一样每个模块都能按需拼接项目越做越顺。

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

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

免费获取报价 →
↑