公司动态
Spring Boot + Vue3全栈实战:从零搭建美食网站项目保姆级教程
最近在辅导学生做毕业设计和课程实践时发现很多同学对如何将一个完整的、前后端分离的Java项目从零搭建起来感到无从下手。特别是涉及Spring Boot后端和Vue3前端的项目虽然网上有源码但环境配置、依赖安装、数据库初始化、前后端联调等环节的“坑”一个接一个导致项目跑不起来严重打击学习积极性。本文将以一个美食网站项目为例提供一份从环境准备到项目成功运行的保姆级全流程搭建教程。无论你是正在寻找Java课设、毕设选题的同学还是希望巩固Spring Boot Vue3全栈技能的开发者都能按照本文的步骤亲手搭建一个可运行、可扩展的现代化Web应用。1. 项目概述与技术栈选型在开始动手之前我们首先要明确我们要构建的是一个什么样的项目以及为什么选择这些技术。1.1 美食网站项目核心功能本项目是一个典型的内容展示与用户交互型网站模拟一个美食分享平台。其核心业务功能模块包括用户模块用户注册、登录、个人信息管理。美食内容模块美食菜谱的发布、浏览、分类检索、详情查看。互动模块用户对菜谱的收藏、点赞、评论。后台管理模块通常独立对用户、菜谱、评论等内容进行增删改查CRUD管理。这是一个非常经典的业务模型涵盖了用户认证、数据增删改查、前后端数据交互、文件上传等Web开发核心技能点非常适合作为学习全栈开发的实战项目。1.2 前后端分离架构与技术栈详解本项目采用目前主流的前后端分离架构。这种架构将前端展示逻辑和后端业务逻辑、数据访问完全解耦通过API接口进行通信使得前后端可以独立开发、部署和扩展。后端技术栈 (Spring Boot)Spring Boot 2.7.x / 3.x: 本文以Spring Boot 2.7.18为例它简化了Spring应用的初始搭建和开发过程提供了自动配置、内嵌Web服务器等特性让我们能快速构建独立运行的、生产级的应用。Spring MVC: 处理HTTP请求实现RESTful API接口。Spring Data JPA / MyBatis-Plus: 作为数据持久层框架。JPA更面向对象MyBatis-Plus则提供了更灵活的SQL能力。本项目示例将使用MyBatis-Plus因为它在国内开发者中流行度高且能简化很多CRUD操作。MySQL 8.0: 关系型数据库用于存储用户、菜谱等结构化数据。Maven: 项目构建和依赖管理工具。前端技术栈 (Vue 3)Vue 3: 当前最新的Vue主流版本提供了更优的性能、更好的TypeScript支持以及Composition API。Vite: 下一代前端构建工具启动速度和热更新远超Webpack开发体验极佳。Element Plus / Ant Design Vue: 流行的UI组件库能快速搭建出美观的界面。本文示例将使用Element Plus。Axios: 基于Promise的HTTP客户端用于前端向后端发起API请求。Vue Router 4: Vue.js官方的路由管理器用于构建单页面应用SPA。Pinia: Vue官方推荐的状态管理库用于管理跨组件的共享状态如用户登录信息。开发工具后端IDE: IntelliJ IDEA (推荐) 或 Eclipse。前端IDE: Visual Studio Code (推荐) 或 WebStorm。数据库工具: Navicat, DBeaver 或 MySQL Workbench。API测试工具: Postman 或 Apifox。2. 本地开发环境准备“工欲善其事必先利其器”。在拉取代码之前请确保你的本地环境已就绪。2.1 基础软件安装与配置Java JDK确保已安装JDK 8或以上版本Spring Boot 2.x兼容JDK 8Spring Boot 3.x需要JDK 17。在命令行输入java -version和javac -version验证。java -version # 应输出类似openjdk version 1.8.0_392Node.js 与 npmVue3开发需要Node.js环境。建议安装LTS长期支持版本如18.x或20.x。安装后自带包管理器npm。node -v # 应输出类似v18.19.0 npm -v # 应输出类似10.2.3建议将npm源切换为国内镜像如淘宝源以加速依赖下载npm config set registry https://registry.npmmirror.comMaven如果你使用IDEA它通常内置了Maven。你也可以独立安装并配置环境变量MAVEN_HOME。在命令行输入mvn -v验证。mvn -v # 应输出Apache Maven版本信息MySQL 8.0下载并安装MySQL 8.0。安装过程中记住你设置的root用户密码。安装完成后启动MySQL服务。2.2 数据库初始化使用数据库连接工具如Navicat或命令行创建一个用于本项目的数据库。连接MySQL服务器主机localhost端口3306用户root密码你的密码。执行SQL语句创建数据库CREATE DATABASE IF NOT EXISTS food_website DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;utf8mb4字符集支持存储Emoji等特殊字符是当前推荐的选择。可选为了安全建议创建一个项目专用的数据库用户并授予其food_website数据库的所有权限。CREATE USER food_userlocalhost IDENTIFIED BY YourStrongPassword123!; GRANT ALL PRIVILEGES ON food_website.* TO food_userlocalhost; FLUSH PRIVILEGES;至此基础环境准备完毕。3. 后端Spring Boot项目搭建与配置我们将从零开始搭建后端项目而不是直接导入源码。理解每一步的配置远比直接运行一个现成项目更重要。3.1 使用Spring Initializr创建项目打开IDEA选择File - New - Project...。左侧选择Spring Initializr。填写项目元数据Project: MavenLanguage: JavaSpring Boot: 2.7.18 (选择一个稳定的版本)Group:com.example(可按需修改如com.yourname)Artifact:food-serverPackaging: JarJava: 8 或 11点击Next在Dependencies中搜索并添加以下依赖Spring Web(构建Web应用包含MVC)MySQL Driver(MySQL数据库驱动)MyBatis Framework(或直接添加MyBatis-Plus的依赖稍后在pom.xml中手动添加更灵活)Lombok(简化Java Bean代码强烈推荐)点击FinishIDEA会自动生成项目并下载初始依赖。3.2 手动补充关键依赖与配置Spring Initializr可能没有MyBatis-Plus的最新版本我们需要手动编辑pom.xml。添加 MyBatis-Plus 依赖在pom.xml的dependencies部分添加!-- MyBatis-Plus 启动器 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version !-- 请查看官网使用最新稳定版 -- /dependency同时可以移除Spring Initializr添加的MyBatis Framework依赖避免冲突。配置数据库连接打开src/main/resources/application.properties文件将其重命名为application.ymlYAML格式更清晰并填入以下配置server: port: 8080 # 后端服务启动端口 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/food_website?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root # 或用你创建的 food_user password: your_password_here # 替换为你的真实密码 jackson: time-zone: GMT8 date-format: yyyy-MM-dd HH:mm:ss mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印SQL开发时有用 global-config: db-config: logic-delete-field: deleted # 全局逻辑删除字段名若需要 logic-delete-value: 1 # 逻辑已删除值 logic-not-delete-value: 0 # 逻辑未删除值 mapper-locations: classpath:mapper/*.xml # XML映射文件位置如果用注解则不需要注意your_password_here务必替换。生产环境中密码应通过环境变量或配置中心管理切勿硬编码。创建核心包结构在src/main/java/com/example/foodserver下创建以下包这是典型的MVC分层结构controller存放控制器处理HTTP请求。service存放业务逻辑层接口。service/impl存放业务逻辑层实现。mapper存放MyBatis-Plus的Mapper接口相当于DAO层。entity存放实体类对应数据库表。dto存放数据传输对象用于接口入参和出参。config存放配置类如跨域配置、拦截器等。common存放通用类如统一响应封装、常量、工具类等。3.3 编写核心业务代码以用户模块为例我们以“用户”实体为例演示如何从实体层到控制器层的完整代码编写。实体类 (Entity)在entity包下创建User.java。使用Lombok注解简化代码。package com.example.foodserver.entity; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.time.LocalDateTime; Data // Lombok注解自动生成getter, setter, toString等方法 TableName(t_user) // 指定对应数据库表名 public class User { TableId(type IdType.AUTO) // 主键自增 private Long id; private String username; // 用户名 private String password; // 密码存储加密后的 private String nickname; // 昵称 private String avatar; // 头像URL private String email; // 邮箱 TableField(fill FieldFill.INSERT) // 插入时自动填充 private LocalDateTime createTime; TableField(fill FieldFill.INSERT_UPDATE) // 插入和更新时自动填充 private LocalDateTime updateTime; // 可以使用 TableLogic 注解实现逻辑删除 // private Integer deleted; }Mapper接口在mapper包下创建UserMapper.java。继承MyBatis-Plus的BaseMapper即可获得基础的CRUD方法。package com.example.foodserver.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.foodserver.entity.User; import org.apache.ibatis.annotations.Mapper; Mapper // 标识为MyBatis的MapperSpring会自动扫描 public interface UserMapper extends BaseMapperUser { // 可以在此定义自定义的复杂SQL方法 // User selectUserWithDetail(Long id); }业务层接口与实现在service包下创建UserService.java接口。package com.example.foodserver.service; import com.baomidou.mybatisplus.extension.service.IService; import com.example.foodserver.entity.User; import com.example.foodserver.dto.UserLoginDTO; import com.example.foodserver.dto.UserRegisterDTO; public interface UserService extends IServiceUser { // 自定义业务方法 User register(UserRegisterDTO userRegisterDTO); String login(UserLoginDTO userLoginDTO); User getCurrentUserInfo(); }在service/impl包下创建UserServiceImpl.java实现类。package com.example.foodserver.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.foodserver.dto.UserLoginDTO; import com.example.foodserver.dto.UserRegisterDTO; import com.example.foodserver.entity.User; import com.example.foodserver.mapper.UserMapper; import com.example.foodserver.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.util.DigestUtils; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; Service Slf4j // Lombok日志注解 public class UserServiceImpl extends ServiceImplUserMapper, User implements UserService { Override public User register(UserRegisterDTO userRegisterDTO) { // 1. 校验用户名是否已存在 LambdaQueryWrapperUser queryWrapper new LambdaQueryWrapper(); queryWrapper.eq(User::getUsername, userRegisterDTO.getUsername()); if (this.getOne(queryWrapper) ! null) { throw new RuntimeException(用户名已存在); } // 2. 密码加密这里使用简单的MD5生产环境应用更安全的BCrypt String encryptedPwd DigestUtils.md5DigestAsHex( userRegisterDTO.getPassword().getBytes(StandardCharsets.UTF_8) ); // 3. DTO转Entity并保存 User user new User(); BeanUtils.copyProperties(userRegisterDTO, user); user.setPassword(encryptedPwd); user.setCreateTime(LocalDateTime.now()); user.setUpdateTime(LocalDateTime.now()); this.save(user); log.info(用户注册成功{}, user.getUsername()); return user; } Override public String login(UserLoginDTO userLoginDTO) { // 1. 根据用户名查询用户 LambdaQueryWrapperUser queryWrapper new LambdaQueryWrapper(); queryWrapper.eq(User::getUsername, userLoginDTO.getUsername()); User user this.getOne(queryWrapper); if (user null) { throw new RuntimeException(用户名或密码错误); } // 2. 校验密码 String encryptedInputPwd DigestUtils.md5DigestAsHex( userLoginDTO.getPassword().getBytes(StandardCharsets.UTF_8) ); if (!encryptedInputPwd.equals(user.getPassword())) { throw new RuntimeException(用户名或密码错误); } // 3. 生成Token此处简化实际应用应使用JWT等无状态方案 // 模拟生成一个Token实际项目中应使用JWT库 String mockToken mock_jwt_token_for_user_ user.getId(); log.info(用户登录成功{}, user.getUsername()); return mockToken; } Override public User getCurrentUserInfo() { // 模拟从ThreadLocal或SecurityContext中获取当前用户ID // 此处为演示直接返回一个模拟用户 // 真实场景Long currentUserId UserContext.getCurrentUserId(); // return this.getById(currentUserId); User mockUser new User(); mockUser.setId(1L); mockUser.setUsername(demoUser); mockUser.setNickname(演示用户); return mockUser; } }控制器 (Controller)在controller包下创建UserController.java。package com.example.foodserver.controller; import com.example.foodserver.common.R; import com.example.foodserver.dto.UserLoginDTO; import com.example.foodserver.dto.UserRegisterDTO; import com.example.foodserver.entity.User; import com.example.foodserver.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; RestController RequestMapping(/user) Slf4j public class UserController { Autowired private UserService userService; PostMapping(/register) public RUser register(RequestBody UserRegisterDTO userRegisterDTO) { log.info(用户注册{}, userRegisterDTO.getUsername()); User user userService.register(userRegisterDTO); return R.success(user); } PostMapping(/login) public RString login(RequestBody UserLoginDTO userLoginDTO) { log.info(用户登录{}, userLoginDTO.getUsername()); String token userService.login(userLoginDTO); return R.success(token); } GetMapping(/info) public RUser getInfo() { User user userService.getCurrentUserInfo(); return R.success(user); } }其中R是一个统一响应结果封装类在common包下创建package com.example.foodserver.common; import lombok.Data; import java.io.Serializable; Data public class RT implements Serializable { private Integer code; // 状态码如 200成功500失败 private String msg; // 提示信息 private T data; // 响应数据 public static T RT success(T data) { RT r new R(); r.code 200; r.msg success; r.data data; return r; } public static T RT error(String msg) { RT r new R(); r.code 500; r.msg msg; return r; } }DTO对象在dto包下创建UserRegisterDTO.java和UserLoginDTO.java。// UserRegisterDTO.java package com.example.foodserver.dto; import lombok.Data; import javax.validation.constraints.NotBlank; Data public class UserRegisterDTO { NotBlank(message 用户名不能为空) private String username; NotBlank(message 密码不能为空) private String password; private String nickname; private String email; } // UserLoginDTO.java package com.example.foodserver.dto; import lombok.Data; import javax.validation.constraints.NotBlank; Data public class UserLoginDTO { NotBlank(message 用户名不能为空) private String username; NotBlank(message 密码不能为空) private String password; }3.4 解决跨域问题与启动测试前后端分离项目前端运行在localhost:5173(Vite默认端口)后端在localhost:8080浏览器会因同源策略阻止请求。需要在后端配置跨域。在config包下创建WebConfig.javapackage com.example.foodserver.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; Configuration public class WebConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) // 对所有接口生效 .allowedOriginPatterns(*) // 允许所有来源生产环境应指定具体前端地址 .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) // 允许携带Cookie等凭证 .maxAge(3600); // 预检请求缓存时间 } }启动后端项目找到FoodServerApplication主类运行main方法。控制台看到Tomcat started on port(s): 8080即表示启动成功。使用Postman测试接口POST http://localhost:8080/user/registerBody (raw JSON):{username:test1, password:123456, nickname:测试用户}POST http://localhost:8080/user/loginBody:{username:test1, password:123456}应能收到成功的响应。4. 前端Vue3项目搭建与开发后端API就绪后我们开始搭建前端项目。4.1 使用Vite创建Vue3项目打开终端如VSCode的终端进入你准备存放前端项目的目录。执行以下命令创建项目npm create vuelatest food-web根据提示进行选择Add TypeScript?-No(为简化本文不用TS)Add JSX Support?-NoAdd Vue Router for Single Page Application development?-YesAdd Pinia for state management?-YesAdd Vitest for Unit Testing?-NoAdd an End-to-End Testing Solution?-NoAdd ESLint for code quality?-Yes(推荐)创建完成后进入项目目录并安装依赖cd food-web npm install安装Element Plus和Axiosnpm install element-plus axios npm install --save-dev element-plus/icons-vue # 图标库4.2 项目基础配置引入Element Plus修改src/main.js(或main.js)。import { createApp } from vue import App from ./App.vue import router from ./router import { createPinia } from pinia // 引入Element Plus import ElementPlus from element-plus import element-plus/dist/index.css // 引入图标组件按需 import * as ElementPlusIconsVue from element-plus/icons-vue const app createApp(App) // 注册所有图标 for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } app.use(createPinia()) app.use(router) app.use(ElementPlus) app.mount(#app)配置Axios实例在src目录下创建utils/request.js文件。import axios from axios // 创建axios实例 const service axios.create({ baseURL: http://localhost:8080, // 后端API基础地址 timeout: 10000 // 请求超时时间 }) // 请求拦截器 service.interceptors.request.use( config { // 在发送请求之前做些什么例如添加token const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer ${token} } return config }, error { // 对请求错误做些什么 console.error(Request Error:, error) return Promise.reject(error) } ) // 响应拦截器 service.interceptors.response.use( response { // 对响应数据做点什么 const res response.data // 假设后端统一返回 {code: 200, data: ..., msg: success} if (res.code 200) { return res.data // 直接返回业务数据 } else { // 业务逻辑错误 console.error(Business Error:, res.msg) // 可以在此处弹出错误提示 return Promise.reject(new Error(res.msg || Error)) } }, error { // 对响应错误做点什么 console.error(Response Error:, error.response?.status, error.message) // 处理HTTP错误如401跳转登录 if (error.response?.status 401) { // 清除token跳转到登录页 localStorage.removeItem(token) router.push(/login) } return Promise.reject(error) } ) export default service4.3 实现用户登录页面修改路由src/router/index.js添加登录页路由。import { createRouter, createWebHistory } from vue-router import Login from ../views/Login.vue const router createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: /, redirect: /login // 默认重定向到登录页 }, { path: /login, name: login, component: Login }, // ... 后续可以添加主页、注册页等路由 ] }) export default router创建登录页面组件src/views/Login.vue。template div classlogin-container el-card classlogin-card template #header div classlogin-header h2美食网站 - 登录/h2 /div /template el-form refloginFormRef :modelloginForm :rulesloginRules label-width80px submit.preventhandleLogin el-form-item label用户名 propusername el-input v-modelloginForm.username placeholder请输入用户名 prefix-iconUser / /el-form-item el-form-item label密码 proppassword el-input v-modelloginForm.password typepassword placeholder请输入密码 prefix-iconLock show-password / /el-form-item el-form-item el-button typeprimary :loadingloading clickhandleLogin stylewidth: 100%; 登录 /el-button /el-form-item div classlogin-footer el-link typeprimary click$router.push(/register)还没有账号去注册/el-link /div /el-form /el-card /div /template script setup import { ref, reactive } from vue import { ElMessage } from element-plus import { useRouter } from vue-router import request from /utils/request // 导入配置好的axios实例 const router useRouter() const loginFormRef ref() const loading ref(false) const loginForm reactive({ username: , password: }) const loginRules { username: [ { required: true, message: 请输入用户名, trigger: blur } ], password: [ { required: true, message: 请输入密码, trigger: blur } ] } const handleLogin () { loginFormRef.value.validate(async (valid) { if (!valid) return loading.value true try { // 调用后端登录接口 const token await request.post(/user/login, loginForm) // 存储token到本地 localStorage.setItem(token, token) ElMessage.success(登录成功) // 跳转到首页 router.push(/home) } catch (error) { ElMessage.error(error.message || 登录失败请检查用户名和密码) } finally { loading.value false } }) } /script style scoped .login-container { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); } .login-card { width: 400px; } .login-header { text-align: center; } .login-footer { text-align: center; margin-top: 20px; } /style启动前端项目在项目根目录运行npm run dev。Vite会启动开发服务器通常地址是http://localhost:5173。访问该地址你应该能看到登录页面。4.4 前后端联调确保后端Spring Boot应用正在运行端口8080。确保前端Vue应用正在运行端口5173。在浏览器中打开http://localhost:5173。在登录页输入之前通过Postman注册的用户名和密码点击登录。观察浏览器开发者工具的Network标签页应该能看到一个向http://localhost:8080/user/login发送的POST请求并且收到包含mock_jwt_token的响应。登录成功后前端会跳转到/home目前是404因为还没创建主页。你可以在localStorage中看到存储的token。至此一个前后端分离的美食网站项目最核心的用户登录注册流程已经打通。你可以按照同样的模式去开发菜谱列表、详情、发布等页面调用相应的后端API。5. 项目运行与部署5.1 后端打包与运行打包在后端项目根目录pom.xml所在目录执行Maven打包命令。mvn clean package -DskipTests成功后会在target目录下生成food-server-0.0.1-SNAPSHOT.jar文件。运行java -jar target/food-server-0.0.1-SNAPSHOT.jar你可以通过--server.port8081参数指定运行端口。5.2 前端打包与部署打包在前端项目根目录执行npm run build打包产物会生成在dist目录下。部署将dist目录下的所有文件复制到你的Web服务器如Nginx, Apache的网站根目录下。并配置Web服务器将所有非静态资源的请求重定向到index.html用于支持Vue Router的history模式。5.3 使用Docker容器化部署进阶对于生产环境使用Docker能极大简化环境一致性问题。后端Dockerfile在Spring Boot项目根目录创建Dockerfile。# 使用官方OpenJDK镜像作为基础镜像 FROM openjdk:11-jre-slim # 维护者信息 LABEL maintaineryour-emailexample.com # 将jar包复制到容器内并重命名为 app.jar COPY target/food-server-*.jar /app.jar # 暴露端口 EXPOSE 8080 # 启动命令 ENTRYPOINT [java, -jar, /app.jar]构建并运行后端镜像# 在项目根目录 docker build -t food-server:latest . docker run -d -p 8080:8080 --name food-server-container food-server:latest前端也可以类似地通过Nginx镜像来部署。6. 常见问题与解决方案在搭建过程中你可能会遇到以下问题问题现象可能原因解决方案后端启动失败报Failed to configure a DataSource数据库连接配置错误或数据库服务未启动。1. 检查application.yml中的数据库URL、用户名、密码。2. 确认MySQL服务已启动。3. 确认数据库food_website已创建。前端npm install失败或极慢网络问题或npm源问题。1. 检查网络连接。2. 执行npm config set registry https://registry.npmmirror.com切换为国内镜像源。3. 删除node_modules和package-lock.json重新执行npm install。前端访问后端API出现CORS错误后端未正确配置跨域或前端请求地址错误。1. 确认后端WebConfig中的跨域配置已生效。2. 确认前端request.js中的baseURL与后端运行地址一致。3. 检查浏览器控制台Network标签查看请求的Origin和Access-Control-Allow-Origin响应头。后端接口返回404接口路径错误或Controller未被Spring扫描到。1. 检查Postman或前端请求的URL路径是否正确包括端口。2. 确认Controller类上有RestController和RequestMapping注解。3. 确认主启动类SpringBootApplication的包路径能扫描到Controller所在的包。MyBatis-Plus插入数据时自动填充字段如createTime不生效未配置元对象处理器。创建一个配置类实现MetaObjectHandler接口并重写insertFill和updateFill方法将其注册为Spring Bean。前端页面空白控制台报错Failed to resolve import ...依赖未正确安装或路径引用错误。1. 检查import语句的路径是否正确。2. 尝试删除node_modules和package-lock.json重新npm install。3. 检查vite.config.js中别名的配置。7. 项目扩展与最佳实践建议完成基础搭建后你可以从以下几个方面深化和优化项目这会让你的项目更完整、更专业也是面试和毕设的加分项。使用更安全的认证授权将模拟Token替换为JWT (JSON Web Token)。后端使用库如jjwt生成和验证Token前端将Token存储在localStorage或Cookie中并在每次请求时通过拦截器携带。密码加密升级不要使用MD5。使用Spring Security的BCryptPasswordEncoder进行密码加密和验证它是目前最推荐的单向哈希算法。接口参数校验在Controller的DTO参数上使用Valid注解并结合NotBlank,Size,Email等注解进行校验统一处理MethodArgumentNotValidException异常返回友好的错误信息。全局异常处理创建一个RestControllerAdvice注解的全局异常处理类统一处理业务异常、系统异常、校验异常等并封装成统一的R对象返回。使用Swagger/OpenAPI生成接口文档引入springdoc-openapi依赖自动生成API文档方便前后端联调和测试。访问http://localhost:8080/swagger-ui.html即可查看。添加日志记录使用SLF4J和Logback合理记录INFO、WARN、ERROR级别的日志便于线上问题排查。数据库连接池与性能默认的HikariCP连接池性能很好但可以调整application.yml中的相关参数如maximum-pool-size,connection-timeout以适应生产环境。前端状态管理对于用户信息、全局配置等数据使用Pinia进行集中管理替代频繁的组件间传参或事件总线。路由守卫在前端使用Vue Router的导航守卫在用户访问需要登录的页面如个人中心时检查本地是否存在有效Token否则重定向到登录页。项目结构规范化随着项目变大遵循良好的目录结构。例如将API请求函数统一放在src/api目录下将公共组件放在src/components/common下。从零开始搭建一个完整的前后端分离项目是掌握现代Web开发流程的关键一步。本文详细拆解了从环境准备、技术选型、后端Spring Boot API开发、前端Vue3页面构建到联调、部署和问题排查的全过程。希望这份保姆级教程能帮助你顺利跑通第一个Spring Boot Vue3项目并以此为基础不断添加新功能探索更优的架构和代码实践。