拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Spring Boot+Vue+MySQL全栈项目实战:从技术选型到部署上线的毕业设计复盘

简介本资源是一套面向计算机专业本科生的Java毕业设计实战项目聚焦游戏分享类Web应用开发完整呈现从需求分析、前后端分离架构到部署上线的全流程实践。项目采用SpringBootVueMySQL技术栈涵盖用户注册登录、游戏资源上传下载等核心功能适合作为课程设计、毕设选题或Java全栈入门学习范例。压缩包共651个文件包含140个Java后端逻辑文件、105个Vue组件与页面、63个JS交互脚本、54个JPG/PNG素材及159个SVG图标辅以SQL建表语句、YML配置、BAT启动脚本和详细说明文档总大小24.22MB模块划分清晰便于理解分层架构与工程组织方式。已有51人学习下载提供可直接运行的完整源码、数据库设计文档及环境配置说明显著降低调试门槛助力开发者快速掌握SpringBoot后端开发规范、Vue单页应用集成及MySQL数据建模实践。1. 项目缘起一个“腾达”游戏分享网站的诞生记最近在整理硬盘翻出来一个压箱底的毕业设计项目名字叫“腾达”游戏分享网站。当时为了这个项目前前后后折腾了小半年从技术选型到功能实现再到最后的论文撰写和答辩踩过的坑、熬过的夜现在想起来都历历在目。这个项目用到的技术栈是当时其实现在也很主流的 Spring Boot Vue MySQL算是一个比较经典的前后端分离架构的实践。今天把它翻出来不是要炒冷饭而是想从一个过来人的角度重新梳理一遍这个项目的设计与实现过程。很多同学在做毕业设计时往往只关注“功能能不能跑通”而忽略了背后的设计思想、技术细节和那些“教科书上不会写”的实战经验。我希望通过这篇分享不仅能让你看懂一个网站是怎么从零到一搭建起来的更能让你理解每一步选择背后的“为什么”以及如何避免那些我当年踩过的坑。无论你是正在为毕业设计发愁的学弟学妹还是想入门全栈开发的新手相信这篇内容都能给你带来一些实实在在的参考。2. 技术选型背后的逻辑为什么是Spring Boot Vue MySQL做任何项目技术选型都是第一步也是最关键的一步。选对了事半功倍选错了可能中途就得推倒重来。当时选择 Spring Boot Vue MySQL 这个组合并不是拍脑袋决定的而是基于毕业设计的核心诉求和当时的技术生态综合考量后的结果。2.1 后端框架Spring Boot 的“约定大于配置”毕业设计的时间通常很紧张我们没有太多精力去从零开始配置一个复杂的 Spring MVC 项目。Spring Boot 的核心优势就在于它的“开箱即用”和“约定大于配置”。它内嵌了 Tomcat 服务器无需单独部署 WAR 包它提供了海量的 Starter 依赖比如spring-boot-starter-web、spring-boot-starter-data-jpa、spring-boot-starter-security等只需要在pom.xml里引入基本的配置就自动完成了。注意Spring Boot 版本的选择很重要。我当时用的是 2.x 版本这是一个长期支持LTS版本社区活跃资料丰富。不建议在毕业设计中盲目追求最新版本比如刚发布的 3.x因为可能会遇到一些未知的兼容性问题而解决这些问题会耗费大量时间。选择成熟稳定的版本是稳妥之举。对于“腾达”游戏分享网站来说后端需要处理用户认证、游戏信息管理、评论、下载等业务逻辑。Spring Boot 配合 Spring Data JPA可以极大地简化数据库操作。JPA 的 Repository 接口让我们几乎不用写 SQL 就能完成增删改查这对于快速原型开发非常友好。当然复杂查询还是需要用到Query注解来写 JPQL 或原生 SQL。2.2 前端框架Vue.js 的渐进式与组件化前端的选择上Vue.js 以其渐进式的特性和易于上手的特点脱颖而出。对于学生项目或中小型应用来说Vue 的学习曲线相对平缓。它的核心库只关注视图层很容易与其它库或已有项目整合。“腾达”网站需要展示游戏列表、详情页、用户中心等这些页面都有明显的组件特征。比如一个游戏卡片组件可以在列表页复用也可以在推荐栏复用。Vue 的单文件组件.vue 文件将模板、脚本和样式封装在一起使得组件的开发和维护非常清晰。我们使用 Vue CLI 来搭建项目骨架它集成了 Webpack、Babel、ESLint 等工具链省去了繁琐的配置。当时也考虑了 React但 React 的学习成本特别是 JSX 和更庞大的生态对新手来说可能更高一些。Vue 的模板语法对从 HTML/CSS/JavaScript 过来的同学更友好。路由管理使用 Vue Router状态管理对于这个规模的毕业设计最初并没有引入 Vuex而是通过组件间通信和简单的全局事件总线来处理后期随着功能复杂才考虑引入这正好体现了 Vue “渐进式” 的理念。2.3 数据库MySQL 的稳定与通用数据库选择 MySQL 几乎是毋庸置疑的。它是世界上最流行的开源关系型数据库资料极其丰富社区庞大遇到任何问题几乎都能找到解决方案。对于毕业设计而言它完全免费安装配置简单有像 Navicat、MySQL Workbench 这样优秀的图形化管理工具。游戏分享网站的数据结构相对规整用户表、游戏表、游戏类型表、评论表、下载记录表等表与表之间存在明确的关系如一对多、多对多。关系型数据库 MySQL 非常适合处理这类结构化数据。我们使用 InnoDB 存储引擎支持事务和外键约束保证了数据的一致性。例如当用户删除账号时通过外键约束可以级联删除其相关的评论和下载记录避免产生垃圾数据。3. 核心功能模块设计与实现拆解“腾达”游戏分享网站主要面向游戏爱好者核心功能围绕“分享”和“发现”展开。下面我分模块拆解关键的设计思路和实现细节。3.1 用户系统从注册登录到权限管理用户系统是任何网站的基础。我们设计了以下几个核心实体用户表 (user)包含 id、用户名、密码加密存储、邮箱、头像、简介、注册时间、状态正常/禁用等字段。角色表 (role)如普通用户、管理员。用户-角色关联表实现用户和角色的多对多关系。密码安全是重中之重。绝对不能在数据库中明文存储密码。我们使用 Spring Security 提供的BCryptPasswordEncoder进行密码加密。它的特点是每次加密生成的哈希值都不同但可以通过matches方法进行验证。这比使用固定盐值的 MD5 或 SHA 要安全得多。// Spring Security 配置类中的 Bean 定义 Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } // 注册时加密密码 user.setPassword(passwordEncoder.encode(rawPassword)); // 登录时验证密码 passwordEncoder.matches(rawPassword, storedEncodedPassword);权限控制采用经典的 RBAC基于角色的访问控制模型。我们使用 Spring Security 进行权限拦截。在配置中我们可以定义哪些 URL 路径需要什么权限。Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers(/admin/**).hasRole(ADMIN) // 管理员后台路径 .antMatchers(/user/**).hasAnyRole(USER, ADMIN) // 用户中心 .antMatchers(/game/upload).authenticated() // 上传游戏需要登录 .antMatchers(/, /game/**, /login, /register).permitAll() // 公开访问 .anyRequest().authenticated() .and() .formLogin().loginPage(/login).permitAll() // 自定义登录页 .and() .logout().permitAll(); }在前端我们可以根据用户角色动态渲染导航栏。例如只有管理员才看得到“后台管理”的链接。3.2 游戏信息管理CRUD与富文本游戏是网站的核心内容。游戏表 (game) 的设计包含id、名称、简介、详情描述、封面图、预览图集、游戏文件下载地址、所属类型、发行商、上传用户ID、上传时间、下载次数、评分等。富文本详情处理游戏详情需要图文混排我们引入了富文本编辑器。前端选用vue-quill-editor它功能丰富且易于集成。这里有一个大坑富文本编辑器提交的 HTML 内容如果直接存入数据库并在前端用v-html渲染会存在严重的 XSS跨站脚本攻击风险。解决方案是前后端双重过滤。前端过滤在vue-quill-editor的配置中可以限制允许的标签和属性但这不够彻底。后端过滤关键在 Spring Boot 后端接收到 HTML 内容后必须进行净化。我们使用Jsoup这个强大的 HTML 解析库来实现白名单过滤。import org.jsoup.Jsoup; import org.jsoup.safety.Safelist; public class HtmlUtils { /** * 使用 Jsoup 白名单过滤 HTML防止 XSS 攻击 * param html 原始HTML * return 净化后的安全HTML */ public static String clean(String html) { if (html null) return ; // 定义白名单允许的标签和属性 Safelist safelist Safelist.relaxed() .addTags(section, article, div, p, br, hr) .addAttributes(a, href, title, target) // 允许a标签的href等属性 .addProtocols(a, href, http, https) // 限制href协议 .addAttributes(img, src, alt, title, width, height) .addProtocols(img, src, http, https, data); // 允许base64图片 return Jsoup.clean(html, safelist); } } // 在保存游戏详情前调用 game.setDetail(HtmlUtils.clean(gameDetailHtml));文件上传游戏封面、预览图和游戏安装包本身都需要上传。我们使用 Spring Boot 的MultipartFile来处理。关键点在于限制文件类型和大小在配置文件中设置。spring: servlet: multipart: max-file-size: 500MB # 游戏安装包可能很大 max-request-size: 600MB防止文件名冲突和目录遍历攻击上传的文件名不能使用用户原始文件名应使用 UUID 重命名并确保路径在指定目录内。public String uploadFile(MultipartFile file, String uploadDir) throws IOException { String originalFilename file.getOriginalFilename(); String fileExtension originalFilename.substring(originalFilename.lastIndexOf(.)); String newFileName UUID.randomUUID().toString() fileExtension; Path uploadPath Paths.get(uploadDir).toAbsolutePath().normalize(); Files.createDirectories(uploadPath); // 创建目录 Path targetLocation uploadPath.resolve(newFileName); // 安全检查确保目标路径仍在 uploadPath 内 if (!targetLocation.normalize().startsWith(uploadPath)) { throw new IOException(无效的文件路径.); } file.transferTo(targetLocation); return newFileName; }提供文件访问上传的文件存储在服务器本地需要通过静态资源映射对外提供访问。在 Spring Boot 中配置Configuration public class WebConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // 将本地文件路径映射到 URL 路径 registry.addResourceHandler(/uploads/**) .addResourceLocations(file: System.getProperty(user.dir) /uploads/); } }3.3 评论与互动系统评论系统增加了网站的粘性。评论表 (comment) 设计为自关联结构以支持楼中楼回复。id,content,user_id,game_id,create_timeparent_id指向父评论的id如果为0或null则为顶级评论。root_id指向所在评论树的根评论id方便查询整棵评论树。后端接口设计POST /api/comments发表评论需登录。请求体包含gameId,content,parentId(可选)。GET /api/comments/game/{gameId}获取某个游戏下的所有评论以树形结构返回。这里涉及到递归查询或一次查询后在内存中组装的逻辑。为了提高效率我们通常一次查出该游戏下的所有评论然后在服务层用 Map 结构组装成树。// 服务层组装评论树示例 public ListCommentVO buildCommentTree(ListComment commentList) { ListCommentVO topLevelComments new ArrayList(); MapLong, CommentVO commentMap new HashMap(); // 第一遍遍历将所有评论转为VO并放入Map for (Comment comment : commentList) { CommentVO vo convertToVO(comment); vo.setReplies(new ArrayList()); // 初始化回复列表 commentMap.put(vo.getId(), vo); } // 第二遍遍历建立父子关系 for (CommentVO vo : commentMap.values()) { if (vo.getParentId() null || vo.getParentId() 0) { topLevelComments.add(vo); // 没有父评论是顶级评论 } else { CommentVO parent commentMap.get(vo.getParentId()); if (parent ! null) { parent.getReplies().add(vo); // 找到父评论加入其回复列表 } } } return topLevelComments; }前端渲染使用 Vue 的递归组件可以非常优雅地渲染这种树形结构。创建一个CommentItem.vue组件在其模板中调用自身来渲染replies。3.4 搜索与筛选功能随着游戏数量增多搜索和筛选必不可少。我们主要提供两种方式关键词全文搜索针对游戏名称和简介。如果数据量不大比如几千条可以直接使用 MySQL 的LIKE语句。但为了更好的性能和分词效果更优的方案是集成 Elasticsearch。考虑到毕业设计的复杂度我们初期使用LIKE但会在设计上为未来扩展留好接口。SELECT * FROM game WHERE name LIKE CONCAT(%, #{keyword}, %) OR intro LIKE CONCAT(%, #{keyword}, %)多条件筛选按游戏类型、发行商、上传时间范围等筛选。这里需要动态构建 SQL 查询条件。我们使用 MyBatis 的动态 SQL 功能if标签来优雅地实现。select idselectByCondition resultMapGameResultMap SELECT * FROM game where if testtypeId ! null AND type_id #{typeId} /if if testpublisher ! null and publisher ! AND publisher LIKE CONCAT(%, #{publisher}, %) /if if teststartTime ! null AND upload_time #{startTime} /if if testendTime ! null AND upload_time #{endTime} /if /where ORDER BY upload_time DESC /select4. 前后端分离架构的协同与联调这是整个项目开发中挑战最大的部分之一。前端Vue运行在 Node.js 服务上通常是localhost:8080后端Spring Boot运行在 Java 服务上通常是localhost:8081。这就涉及到跨域CORS问题、API 接口规范、以及高效的联调流程。4.1 解决跨域问题浏览器出于安全考虑默认禁止前端页面向不同域名或同域名不同端口的服务器发起请求。我们需要在后端显式地允许跨域。在 Spring Boot 中可以创建一个全局的 CORS 配置类Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) // 针对所有/api开头的接口 .allowedOrigins(http://localhost:8080) // 允许前端地址 .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) // 允许携带cookie等凭证 .maxAge(3600); } }提示在生产环境中allowedOrigins应该替换为实际的前端域名而不是*允许所有以增强安全性。4.2 统一的API响应格式前后端约定一个统一的 JSON 响应格式能让联调和错误处理变得非常清晰。我们定义了一个通用的Result类。Data NoArgsConstructor AllArgsConstructor public class ResultT { private Integer code; // 状态码如 200成功500服务器错误400客户端错误 private String message; // 提示信息 private T data; // 返回的数据 public static T ResultT success(T data) { return new Result(200, 操作成功, data); } public static T ResultT success(String message, T data) { return new Result(200, message, data); } public static Result? error(Integer code, String message) { return new Result(code, message, null); } public static Result? error(String message) { return new Result(500, message, null); } }控制器中统一返回Result对象PostMapping(/login) public ResultUserVO login(RequestBody LoginForm form) { UserVO user userService.login(form); return Result.success(登录成功, user); } GetMapping(/game/{id}) public ResultGameDetailVO getGame(PathVariable Long id) { GameDetailVO game gameService.getGameDetail(id); if (game null) { return Result.error(404, 游戏不存在); } return Result.success(game); }前端Vue使用 Axios 拦截器统一处理响应// request.js import axios from axios; const service axios.create({ baseURL: process.env.VUE_APP_BASE_API, // 从环境变量读取后端地址 timeout: 10000 }); // 响应拦截器 service.interceptors.response.use( response { const res response.data; // 如果后端返回的 code 不是 200则判断为错误 if (res.code ! 200) { // 统一弹出错误提示 Message.error(res.message || 请求失败); // 可以针对特定code做处理如 401 跳转到登录页 if (res.code 401) { router.push(/login); } return Promise.reject(new Error(res.message || Error)); } else { // 成功则直接返回 data 部分 return res.data; } }, error { // 处理网络错误或服务器错误 Message.error(error.message || 网络异常); return Promise.reject(error); } ); export default service;4.3 状态管理与路由守卫随着应用复杂组件间需要共享的状态如用户登录信息变多。虽然最初没用 Vuex但后来我们还是引入了。它提供了一个集中式的存储方便管理和调试。用户登录状态管理登录成功后后端返回用户信息和 Token如 JWT。前端将 Token 存储在localStorage或sessionStorage中并将用户信息存入 Vuex。在 Axios 的请求拦截器中每次请求自动在 Header 中带上 Token。service.interceptors.request.use( config { const token store.state.user.token; if (token) { config.headers[Authorization] Bearer token; } return config; }, error { return Promise.reject(error); } );路由守卫对于需要登录才能访问的页面如/user/center,/game/upload使用 Vue Router 的导航守卫进行拦截。router.beforeEach((to, from, next) { const isAuthenticated store.state.user.token; // 检查是否有token if (to.matched.some(record record.meta.requiresAuth)) { // 此路由需要认证 if (!isAuthenticated) { next({ path: /login, query: { redirect: to.fullPath } }); } else { next(); } } else { next(); // 不需要认证直接放行 } });5. 数据库设计与性能考量初探数据库设计的好坏直接影响应用的性能和扩展性。这里分享几个在“腾达”项目中用到的设计技巧和优化思路。5.1 表结构设计要点主键与索引每张表都有一个自增的id作为主键。在经常用于查询条件的字段上建立索引能极大提升查询速度。例如user表的username登录用和email找回密码用字段应建立唯一索引。game表的type_id按类型筛选、upload_time按时间排序字段应建立普通索引。comment表的game_id查某游戏的评论和user_id查某用户的评论应建立索引。但索引不是越多越好它会降低写入INSERT/UPDATE/DELETE速度因为索引也需要维护。需要权衡。字段类型选择varchar用于可变长度字符串如用户名、标题需指定合理长度如username varchar(50)。text或longtext用于长文本如游戏详情。对于只有固定几个值的字段如用户状态0正常1禁用使用tinyint比varchar更高效。存储时间戳使用datetime或timestamp。timestamp范围较小但有时区转换功能datetime范围更大。我们统一用了datetime。关联关系与冗余核心关系如评论属于某个用户必须用外键 (user_id) 关联。为了性能有时可以适当冗余。例如在comment表中除了user_id还可以冗余存储username和user_avatar。这样在显示评论列表时就不需要再去连user表查询用户信息用空间换时间。但要注意当用户修改用户名或头像时需要同步更新所有相关评论中的冗余字段这增加了业务逻辑的复杂性。毕业设计中数据量小可以不冗余直接联表查询。5.2 基础SQL优化实践即使不涉及复杂的分布式一些简单的优化也能让应用更流畅。**避免 SELECT ***只查询需要的字段。SELECT *会查询所有列包括可能很大的text字段网络传输和内存占用都更大。-- 不好 SELECT * FROM game WHERE id 1; -- 更好 SELECT id, name, cover_url, intro FROM game WHERE id 1;使用 EXPLAIN 分析查询在复杂的查询语句前加上EXPLAIN可以查看 MySQL 的执行计划了解是否用到了索引有没有全表扫描。EXPLAIN SELECT * FROM game WHERE type_id 3 ORDER BY upload_time DESC;关注type列ALL表示全表扫描需要优化ref或range表示使用了索引。分页查询优化游戏列表、评论列表都需要分页。最常用的LIMIT offset, size在 offset 很大时比如翻到第1000页会非常慢因为 MySQL 需要先扫描并跳过前面 offset 条记录。优化方案使用“游标分页”或“基于ID的分页”。假设列表按上传时间倒序排列-- 传统分页offset大时慢 SELECT * FROM game ORDER BY upload_time DESC, id DESC LIMIT 10000, 20; -- 优化记录上一页最后一条记录的 upload_time 和 id SELECT * FROM game WHERE upload_time 上一页最后的时间 OR (upload_time 上一页最后的时间 AND id 上一页最后的ID) ORDER BY upload_time DESC, id DESC LIMIT 20;这样无论翻到第几页查询速度都很快。6. 部署上线从本地开发到服务器运行毕业设计答辩时如果能有一个在公网可访问的演示地址绝对是加分项。将 Spring Boot Vue MySQL 项目部署到云服务器是最后一个关键环节。6.1 准备工作云服务器与域名购买云服务器可以选择阿里云、腾讯云等厂商的学生机或轻量应用服务器成本很低。选择 CentOS 或 Ubuntu 系统。配置安全组在云服务器控制台开放必要端口22 (SSH)、80 (HTTP)、443 (HTTPS)、3306 (MySQL建议仅对本地IP开放或改为非默认端口以提高安全性)。域名可选可以购买一个便宜的域名并解析到你的服务器 IP。没有域名直接用 IP 访问也可以。6.2 后端 Spring Boot 应用部署Spring Boot 项目打包非常方便。在项目根目录执行mvn clean package -DskipTests会在target目录下生成一个your-project-0.0.1-SNAPSHOT.jar文件。这个 Jar 包是“可执行的”它内嵌了 Tomcat 服务器。将 Jar 包上传到服务器使用 scp 或 SFTP 工具scp target/your-project.jar rootyour-server-ip:/home/app/在服务器上运行cd /home/app # 最简单的方式但关闭终端程序会停止 java -jar your-project.jar # 推荐使用 nohup 在后台运行并将日志输出到文件 nohup java -jar your-project.jar app.log 21 # 或者使用 systemd 来管理更专业以Ubuntu为例 # 1. 创建服务文件 sudo vim /etc/systemd/system/tengda.service # 2. 写入以下内容 [Unit] DescriptionTengda Game Share Backend Service Afternetwork.target [Service] Typesimple Userappuser # 建议创建一个非root用户来运行 WorkingDirectory/home/app ExecStart/usr/bin/java -jar your-project.jar Restarton-failure [Install] WantedBymulti-user.target # 3. 启动并设置开机自启 sudo systemctl daemon-reload sudo systemctl start tengda sudo systemctl enable tengda sudo systemctl status tengda # 查看状态关键配置调整在application.properties或application.yml中需要将数据库连接地址、文件上传路径等改为生产环境的值。可以通过在运行 Jar 包时指定--spring.profiles.activeprod来激活生产环境配置文件。6.3 前端 Vue 应用部署Vue 项目需要先打包成静态文件。npm run build执行后会在项目根目录生成一个dist文件夹里面就是所有的 HTML、CSS、JavaScript 文件。我们需要一个 Web 服务器来托管这些静态文件。最常用的是 Nginx。在服务器上安装 Nginx(Ubuntu)sudo apt update sudo apt install nginx配置 Nginxsudo vim /etc/nginx/sites-available/tengda-frontend写入以下配置server { listen 80; server_name your-domain.com; # 你的域名或服务器IP # 静态文件根目录 root /var/www/tengda-frontend/dist; index index.html; location / { try_files $uri $uri/ /index.html; # 支持Vue Router的history模式 } # 反向代理API请求到后端Spring Boot应用 location /api/ { proxy_pass http://localhost:8081/; # 假设后端运行在8081端口 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 代理上传的文件访问路径 location /uploads/ { proxy_pass http://localhost:8081/uploads/; } }创建软链接并重启 Nginxsudo ln -s /etc/nginx/sites-available/tengda-frontend /etc/nginx/sites-enabled/ sudo nginx -t # 测试配置语法 sudo systemctl restart nginx上传文件将本地的dist文件夹内容上传到服务器的/var/www/tengda-frontend/dist目录。现在访问你的服务器 IP 或域名应该就能看到网站了。所有/api/开头的请求会被 Nginx 转发到后端的 Spring Boot 应用实现了前后端在同一域名下的无缝对接。6.4 MySQL 数据库部署与数据迁移在服务器上安装 MySQLsudo apt install mysql-server sudo mysql_secure_installation # 运行安全脚本设置root密码等登录 MySQL创建数据库和用户CREATE DATABASE tengda CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER tengda_userlocalhost IDENTIFIED BY StrongPassword123!; GRANT ALL PRIVILEGES ON tengda.* TO tengda_userlocalhost; FLUSH PRIVILEGES;将本地开发数据库的数据导出备份mysqldump -u root -p your_local_database tengda_backup.sql将备份文件上传到服务器并导入mysql -u tengda_user -p tengda tengda_backup.sql最后别忘了修改 Spring Boot 生产环境配置文件中的数据库连接信息指向服务器的 MySQL 实例。7. 开发与部署中的“血泪”经验谈回顾整个项目有几个坑是印象特别深刻的也是新手最容易栽跟头的地方。1. 端口冲突与占用问题本地开发时Spring Boot 默认用 8080 端口Vue CLI 开发服务器也用 8080 端口直接冲突。要么改 Vue 的端口在vue.config.js里配置devServer.port要么改 Spring Boot 的端口server.port。部署时也要确保服务器上的端口没有被其他程序占用。2. 路径问题绝对路径 vs 相对路径在代码中引用资源如图片、文件时尽量使用相对路径或通过配置文件定义的路径。特别是在文件上传功能中保存路径和访问 URL 的映射一定要清晰。我在开发时把文件上传到了D:/uploads/结果部署到 Linux 服务器上路径完全不对导致图片404。后来统一在配置文件中定义了一个基础路径file.upload-dir根据操作系统动态拼接。3. 环境配置分离开发、测试、生产环境的配置数据库地址、密钥、第三方API密钥等一定不能写死在代码里。Spring Boot 支持多环境配置文件如application-dev.yml,application-prod.yml通过spring.profiles.active指定激活哪个。敏感信息甚至可以放在环境变量中。4. 日志的重要性别再用System.out.println()了。在 Spring Boot 中集成 SLF4J Logback合理配置日志级别和输出格式。在application.yml中配置logging: level: com.yourpackage: DEBUG # 你的业务代码用DEBUG org.springframework.web: INFO org.hibernate: ERROR # Hibernate日志太多调高等级 file: name: logs/app.log pattern: console: %d{yyyy-MM-dd HH:mm:ss} - %msg%n file: %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n当线上出现问题时查看日志文件是定位问题的第一步。5. 前端资源缓存与更新Vue 打包生成的 JS 和 CSS 文件默认会带哈希值如app.abc123.js这能很好地解决缓存问题。但 HTML 文件index.html通常不被缓存。部署更新后用户可能因为浏览器缓存了旧的 JS 文件而遇到问题。可以在 Nginx 配置中为静态资源设置合适的缓存策略并为index.html设置Cache-Control: no-cache。这个“腾达”游戏分享网站的项目虽然只是一个毕业设计但完整地走了一遍现代 Web 应用从设计、开发到部署的全流程。过程中对 Spring Boot 的自动配置、Vue 的响应式与组件化、MySQL 的优化以及前后端协同有了更深的体会。最大的收获不是做出了一个能跑的系统而是在解决一个个具体问题中积累的实战经验和系统性思维。希望这篇超详细的复盘能帮你绕过那些我踩过的坑更顺畅地完成你自己的项目。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门