ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Spring Boot + Vue 3全栈项目新建与联调部署实践指南

Spring Boot + Vue 3全栈项目新建与联调部署实践指南

1. 为什么我把Spring Boot + Vue当作个人项目的标准起手式

说实话,第一次用Spring Boot + Vue搭项目的时候,我根本没想那么多。后端用Spring Boot因为简历上写着Java,前端用Vue因为教程里都这么配。可真等自己在几个项目里摸爬滚打过之后,才慢慢意识到这套组合的优势不是某单个框架多厉害,而是整套链路足够成熟稳定,从新建项目到最终部署,每一个环节都有大量现成的方案可参考,遇到问题搜索一下基本都能解决。

这篇是系列文章的第一篇,主题就两个字:新建。后面我还会陆续写联调、打包、部署、服务器配置这些内容。之所以把“新建”单独拿出来讲,是因为太多人在这个环节就埋下了坑。比如后端端口用默认8080不规划,前端用Vite默认5173不调整,项目名随手取个test,等到部署的时候才发现一堆地方要返工。我见过有人前后端联调跨域问题卡了整整两天,原因仅仅是代理路径少了一个斜杠。这些坑,大多数在新建项目阶段就能避免。

本篇适合的读者有两类:一类是刚接触全栈开发、想完整走一遍项目流程的学生或转行者;另一类是已经在写代码但习惯直接拉模板改、从没从头理过新建流程的开发者。前者可以按步骤跟着做一遍,后者可以重点看第4节和第5节,那是比较容易忽略的部分。

我在这篇文章里选用的工具链是:JDK 17、Spring Boot 3.2.x、Vue 3 + Vite、npm。这套组合是当前非常主流的选择,网上资料丰富,坑也基本都被踩平了。如果你用的是更新的版本或者更老的稳定版,核心思路完全一样,只是个别参数不同。

2. 后端新建全过程:Spring Initializr不是填完就没事了

2.1 初始化参数的选型,每个字段都值得想清楚再填

打开 start.spring.io ,页面很简洁,但每一个下拉框都不要随手选。我见过太多人直接默认点Generate,结果生成出来的项目Group是com.example,Artifact是demo,Java version是17,最后所有代码都要改包名,麻烦得很。

先看几个关键字段:

  • Project选择Maven。Gradle也很好,但Maven的普及率更高,部署文档多、排错资料多,个人项目用Maven足够。
  • Language选Java。Kotlin虽然优雅,但刚起步不建议给自己加语言负担。
  • Spring Boot版本选3.2.x,不要选3.4.x这类非常新的版本。原因很简单:稳定版本对应的第三方库兼容资料最多,遇到问题最容易被搜到解决方案。
  • Group填你的公司域名反写或者个人域名,没有域名就填io.github.你的名字这种格式。这里有个容易被忽略的点:Group一旦定了,后面改动就是全局的包路径变更,牵连所有类文件的位置,很痛苦。
  • Artifact就是项目名,建议全小写、不要带下划线。比如要做一个博客系统,Artifact填blog-server或blogservice都行,避免带横杠或下划线是因为Maven的groupId、artifactId和最终jar包名直接挂钩,特殊字符在部署脚本里容易出问题。

Java Version选17。Spring Boot 3.x强制要求Java 17以上,同时17又是当前生产环境最普遍使用的LTS版本。选21也不是不行,但很多云服务器上的JDK默认还没切到21,部署时还得额外装一次,没必要在新项目阶段给自己添堵。

2.2 依赖只选用得上的,别让默认项拖累项目

Spring Initializr里面Dependencies能勾很多,但新建项目阶段我的原则是:只选当前明确用得上的,其他后面再加。

一个标准的Spring Boot后端项目,第一步至少需要以下三项:

  • Spring Web:提供内嵌Tomcat和Spring MVC,这是后端对外提供HTTP接口的基础,没有它什么都跑不起来。
  • Lombok:用注解替代写getter、setter、构造函数,每天能省几百行样板代码,从项目第一天就该装。
  • Spring Boot DevTools:开发热重启工具。改完代码按一下Ctrl+F9自动重启,不用手动停服务再启动,开发效率提升明显。

有人习惯一开始就勾上Spring Data JPA、MySQL Driver、Redis、Security这些,我建议先忍住。以Spring Security为例,一旦勾上,你所有接口都会被默认拦截,需要马上写Security配置类放行接口。本来就只想先起个服务看看Hello World,结果因为依赖有一个,登录认证的问题先找上门来了,纯属自己给自己加戏。JPA也一样,数据库连接没配好的时候项目启动直接报错,一个简单的新建项目变成数据库排错现场。

依赖尽量保持最小化,等真正需要哪块功能的时候再用Maven命令或者IDE的依赖管理工具往里加。一条依赖加进去很容易,但一个被Security拦截的接口排查起来很让人头疼。

2.3 IDE导入和目录细节,确认三个地方就够了

项目下载解压之后,用IDEA打开。这里我想说一下很多新手容易摸不着头脑的地方:打开是选择项目根目录,也就是pom.xml所在的那一层,别选到src目录里面去了。IDEA会自动识别Maven项目并开始下载依赖,第一次下载会比较慢,如果网络状况一般,等一会儿是正常的。

依赖下载完成后,等右下角的索引构建完毕,就可以看目录结构了。一个新建的Spring Boot项目会生成src/main/java、src/main/resources和src/test三个核心目录。在src/main/resources下面,有一个文件叫application.properties(如果你初始化时没特别选择,就是properties格式),后面所有配置项都在这里写。

文件结构确认清楚之后,在本地把项目跑起来验证一次。最简单的办法是直接在IDEA里点运行主类,主类就是项目名+Application结尾的那个类,通常位于Group和Artifact拼出来的包路径下。右键运行,看到控制台输出包含"Tomcat started on port 8080"之类的内容,就说明后端骨架已经通了。

如果打开浏览器访问http://localhost:8080,看到的页面是Whitelabel Error Page,不用慌,这只是说明Tomcat在正常工作,只是当前还没有任何接口。程序本身没问题。

3. 前端新建过程:Vite + Vue 3的初始化比想象中更重要

3.1 为什么从Vite起步是明智的,而不是Webpack

现在创建Vue项目,主流工具是Vite,不是Vue CLI。Vue CLI基于Webpack,每次冷启动都要等Webpack把整个应用编译完,项目稍微大点就是几十秒的等待。Vite利用浏览器原生ES Module特性,开发服务器启动速度极快,改动代码后通过HMR热更新几乎是秒级反馈,这在开发体验上完全是两个时代的东西。

如果你的项目将来要部署到Nginx上跑,Vite打包之后生成的dist目录也是标准静态文件,一个简单的server块就能处理。不存在什么特殊的服务端依赖,相对省心。

还有一点是Vite官方模板现在是Vue 3的标准推荐,新项目一上来就是组合式API的写法。组合式API虽然刚接触时觉得"怎么跟Vue 2不一样",但用两天就会习惯,而且它比Options API更适合逻辑复用。既然是新项目,没必要为了迎合旧习惯去选Vue 2。

3.2 创建命令和交互式选项,照着抄就行

在终端里执行:

npm create vite@latest frontend -- --template vue

这行命令的含义是:使用Vite工具,在当前目录下创建一个名为frontend的新项目,模板类型为vue。如果你需要在交互式界面里手动选框架,先去掉--template参数,然后按上下键选择Vue。

创建完成后,进入项目目录:

cd frontend npm install npm run dev

整个过程npm会给一堆提示和大字输出,不用太在意。关键在于最后一步执行npm run dev之后,终端会显示一个本地访问地址,默认是http://localhost:5173。打开浏览器看到Vite + Vue的默认欢迎页,说明前端项目新建成功。

这里有个实际经验要说:npm install安装依赖可能比较慢。这不是命令卡住了,是npm在拉取依赖包。如果等了几分钟还没完成,可以考虑换用淘宝镜像源,执行npm config set registry https://registry.npmmirror.com之后重新install。这一步在后续部署环节很重要,因为服务器上装依赖同样会遇到这个问题。

3.3 前端项目结构,新建后必须先理清三个文件

Vite创建的项目默认目录结构非常简洁,但有几个文件在新建后需要第一时间打开确认。

首先是根目录下的vite.config.js,这是Vite的配置文件。刚生成时它只包含基础的插件注册,后面所有关于端口、代理、构建的配置都加在这里。其次是src/main.js,这是前端应用的入口文件。Vue 3里面创建应用对象的代码和Vue 2差异很大,如果你之前熟悉的是Vue 2写法,看这个文件能最快感受到区别。最后是package.json,里面是项目依赖和脚本命令。注意scripts字段,dev是开发启动命令,build是打包命令,deploy或者预览相关的后续也会在这里扩展。

src目录下还有components文件夹。新建项目阶段,建议先不要改里面默认的HelloWorld组件,也不要急着写业务代码。第一件事是让这个项目保持"干净状态"跑通开发服务器,确认无需任何修改就能正常显示页面。这一步的意义是确认环境本身没问题,避免后续把环境问题混在一起排查。

4. 前后端并联验证:新建项目第一阶段就该建立联调习惯

4.1 两个服务分开启动,先搞清楚各自的"跑道"

很多教程喜欢把前端后端启动流程放在一起说,但实际操作中它们是两个独立的进程。后端跑在8080端口,前端跑在5173端口。启动顺序没有严格要求,我习惯先启动后端,再启动前端。

后端启动:IDEA里直接运行主类,观察控制台输出,确认"Started xxxApplication in x.xx seconds"这样的日志出现。如果没有,看看是不是数据库连接报错。新项目如果没加任何数据库依赖,理论上不会出现这种情况。

前端启动:在frontend目录下执行npm run dev。如果你已经启动过,端口5173可能会被占,Vite会自动切换端口生成5174之类的地址,运行日志会写清楚。浏览器里打开前端地址,看到默认页面就说明前端进程正常。

此时前后端虽然在各自跑,但互相没有任何关联。前端页面是一个静态页面,后端也还没提供任何接口。为了让它们形成有效通信,需要做下一步配置。

4.2 Vite代理配置,比后端CORS更省心的联调方案

前后端直接通信会遇到跨域问题,因为端口不一致,浏览器会拦截响应。解决方案有两种:后端配置CORS,或者前端配置代理。我推荐在开发阶段用前端代理,这也是Vite内置的能力。

在vite.config.js里补充server配置:

import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } })

这段配置的含义是:当浏览器请求前端地址下以/api开头的路径时,Vite开发服务器会把请求转发到http://localhost:8080。转发过程发生在服务器端,浏览器感知不到跨域,因为它在浏览器看来只是请求了同一个域名下的/api路径。

设置代理后,改动vite.config.js需要重启开发服务器才能生效。重启就是Ctrl+C停止npm run dev,再重新执行一次。这个细节经常有人忽略,改完配置发现没效果,排查半天。

4.3 一个后端接口,验证前后端连通性

为了验证代理和前后端通信是否正常,在后端写一个最简单的测试接口。新建一个Controller类:

import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/api/hello") public String hello() { return "Hello from Spring Boot"; } }

注意路径是/api/hello,不是/hello。因为前端代理规则是按/api前缀匹配的,接口路径必须带上这个前缀才能被正确转发。

重启后端服务,然后在前端代码里发起请求验证。在App.vue或者src下任意组件的script部分加一段:

fetch('/api/hello') .then(response => response.text()) .then(data => { console.log(data) alert(data) })

打开浏览器控制台,如果能输出Hello from Spring Boot,整个链路就通了。链路是:浏览器请求5173的/api/hello,Vite代理解析后将请求转发到8080端口的Spring Boot服务,响应原路返回。

这一步做完,前后端新建项目的核心工作就完成了。后续不管是开发业务接口还是编写页面组件,都是在这个打通的基础上进行。

5. 新建项目阶段就该做的部署预规划,别等上线再补

5.1 端口和上下文路径,现在定好省掉后来的迁移痛苦

部署到服务器上和本地开发最大的区别在于:服务器上通常不止一个应用跑在同一台机器里。比如一台2核4G的云服务器,可能要同时跑后端jar包、前端Nginx、MySQL数据库、Redis缓存。端口冲突在部署阶段是很常见的事情。

新建项目阶段,你不需要立刻把端口改成非默认值,但建议在项目里形成一个惯例:后端端口不写死、可配置。Spring Boot的端口配置在启动时可以动态指定,执行java -jar时用--server.port=8081就能覆盖默认值。所以开发阶段不必专门去改application.properties,了解这个机制就行。

更好的做法是,从一开始就在application.properties里使用环境变量占位符来安排关键配置位置:

server.port=${SERVER_PORT:8080}

这行配置的意思是:优先读取环境变量SERVER_PORT,如果不存在就默认8080。部署时在服务器上设置环境变量来切换端口,比改配置文件再重新打包要灵活得多。同样的方式可以套用到数据库地址、Redis地址等所有环境差异配置上。

5.2 前端打包路径,部署时最容易翻车的地方

Vite项目默认构建时资源的引用路径是绝对路径,这在部署到域名根路径时没有任何问题。但如果前后端用的是同源部署(这是后面文章要重点讲的一种部署模式),前端页面可能被挂载在某个子路径下,比如https://example.com/blog/,那么打包就必须指定base路径。

新建项目阶段你可以不处理这个细节,但必须先知道vite.config.js里有base这个配置项。打包部署时如果需要调整,只需改成:

export default defineConfig({ base: '/blog/', plugins: [vue()] })

不提前做好这个心理准备会导致什么结果?我第一次部署时,前端页面白屏,打开控制台全是静态资源404。排查到最后发现就是base没配置,页面入口文件引用的是/js/xxx.js,实际文件放在/blog/js/xxx.js,所有资源全部丢失。这个问题和跨域问题一样,都是部署阶段最常见的翻车点。

5.3 开发和生产两套环境配置,从第一天就分开

一个完整的项目从开发到上线要经历三个阶段:本地开发环境、测试环境、生产环境。每个环境里数据库地址不同,日志级别不同,调试开关不同。

Spring Boot对多环境配置的原生支持很成熟。在resources目录下,除了application.properties,还可以建立application-dev.properties和application-prod.properties。主配置里写公共内容,环境特定的配置放到对应文件里,切换环境时只需要在启动命令里指定:

java -jar app.jar --spring.profiles.active=prod

本地IDEA启动时则在运行配置的VM options里加-Dspring.profiles.active=dev。

前端也同理。Vite支持在项目根目录创建.env.development和.env.production文件,里面以VITE_为前缀定义变量,例如VITE_API_BASE_URL。在代码里通过import.meta.env.VITE_API_BASE_URL读取,不同环境下打包会自动注入对应值。这样新建项目阶段就把配置的"爪子"埋好,后面写业务代码时就不会有环境判断逻辑到处乱飞的问题。

5.4 系列下一篇的预告:从本地到服务器的关键步骤

新建项目只是第一步。按我自己的经验,接下来要处理的事情包括:后端CORS和拦截器配置、前端路由模式选择(hash还是history)、前后端打包流程、Nginx反向代理配置、SSL证书处理,以及云服务器的Java环境和MySQL安装。

做全栈开发这些年最大的体会是:项目能不能顺利上线,不在于某个单点技术用得多牛,而在于整个链路的每一步都清晰可控。新建项目看似简单,实际上是决定后续所有环节顺畅与否的地基。如果你也把这条链路走完,会发现"部署"这件事并没有想象中那么神秘,它更像是在做一套标准化的交接流程,把项目从开发机温柔地搬到服务器上。

我在实际带人做项目时有个习惯,要求新项目第一天的验收标准就是前后端在本地能通过代理完成一次请求响应。谁做到了这一步,谁就已经进入了"全栈开发"的状态。下一步,我们就要开始往这个骨架上填充真实的业务代码了。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进