SpringBoot+Vue实现的在线音乐网站工程包(含MySQL脚本、图片资源与部署指南)

SpringBoot+Vue实现的在线音乐网站工程包(含MySQL脚本、图片资源与部署指南) 本文还有配套的精品资源点击获取简介一个功能完整的在线音乐平台源码包后端用SpringBoot开发封装了用户注册登录、歌曲上传管理、播放列表创建、关键词搜索、歌曲收藏等常用接口前端基于Vue.js构建采用Vue Router做路由跳转Vuex管理全局状态界面适配PC和手机屏幕所有UI图标、用户头像、专辑封面等30多张图片资源已直接内置在项目中压缩包里包含前后端全部源代码已排除.git目录干扰文件、详细README说明文档、MySQL建表语句及初始化数据SQL文件项目结构遵循标准分层规范Controller/Service/DAOVue组件按功能模块划分清晰支持主流IDE如IntelliJ IDEA、VS Code一键导入本地启动调试方便也提供基础Linux服务器部署步骤说明适合计算机专业学生快速上手课程设计、实训项目或毕业设计。1. 项目概述这不是一个“玩具Demo”而是一套能真正跑起来的音乐平台骨架我带过六届计算机专业的毕业设计每年都会收到几十份“在线音乐网站”的选题。但绝大多数学生交上来的是一个只有登录页和三首假数据的Vue页面后端连数据库连接都没配通——不是他们不想做而是网上能找到的所谓“完整项目”要么缺SQL脚本、要么前端路由崩了、要么图片路径全错、要么部署文档写着“自行配置Nginx”结果学生卡在第一步就放弃了。这个SpringBootVue音乐网站工程包是我去年帮三个本科生做毕设时从零开始搭出来、反复压测过三个月的真实生产级教学骨架。它不追求炫酷动画或微服务架构但每一条接口都经过Postman逐个验证每一个Vue组件都支持热重载调试MySQL脚本执行后直接有200条真实测试数据含用户、歌曲、专辑、收藏关系所有图片资源——从用户头像、专辑封面到导航图标——全部按规范路径内置连img src/assets/images/tou.jpg这种引用都不会报404。关键词里提到的“SpringBoot音乐系统”“Vuex音乐前端”“MySQL音乐数据库”不是标签堆砌而是每个词都对应着可触摸的实现比如“Vuex音乐前端”意味着播放控制状态当前播放ID、播放进度、是否暂停全局唯一维护切歌、暂停、拖拽进度条不会出现状态错乱“MySQL音乐数据库”不是简单建个user表而是设计了song含音频URL、时长、格式、上传时间、album支持多歌手关联、user_collection用户-歌曲多对多中间表等7张表并通过外键约束和索引优化保障搜索响应速度。它适合谁如果你是大三刚学完Java Web和Vue基础的学生想两周内交出一份体面的课程设计如果你是毕设开题被导师质疑“功能太单薄”的同学需要一个有真实业务逻辑如收藏夹自动同步、搜索关键词高亮、播放历史记录的底座来扩展或者你是自学全栈的新手厌倦了看教程写todo list想直接在一个有血有肉的音乐场景里练手——那这套工程包就是为你准备的。它不教你“什么是MVC”但你会在UserController.java里看到如何用Valid校验手机号格式在SongService.java里看到事务注解Transactional如何保证上传歌曲更新专辑统计数的原子性你也不会在文档里读到“Vuex是状态管理工具”但当你修改store/modules/player.js里的currentSongId整个应用所有播放按钮的状态会实时联动变化。这就是区别它不是知识图谱而是一台已经组装好、加满油、钥匙就在你手里的车。2. 整体架构设计与分层逻辑为什么这样拆而不是那样拆2.1 后端SpringBoot模块划分拒绝“一锅炖”把业务复杂度关进笼子很多学生初学SpringBoot习惯把所有代码塞进一个Controller里查用户、查歌、处理上传、返回JSON全在一个方法里用if-else判断。这套工程包的后端结构是我在企业真实项目中验证过的分层范式它解决的不是“能不能跑”而是“后续加功能会不会崩溃”。整个后端按标准三层工具层组织Controller层入口守门员只做三件事——接收HTTP请求参数RequestBody或RequestParam、调用Service方法、封装统一响应体ResultT。绝不处理业务逻辑绝不操作数据库。比如SongController.uploadSong()方法它只负责接收MultipartFile file和SongDTO songDTO校验文件大小≤50MB、格式仅mp3/wav、然后把干净的数据交给Service。这里有个关键细节所有Controller方法都加了RestController和RequestMapping(/api)前缀确保所有接口自动带上/api路径避免前端Axios请求时到处拼接baseURL。Service层业务大脑这才是真正的逻辑中枢。它被拆成UserService、SongService、CollectionService等独立类每个类只专注一个领域。比如SongService里有个核心方法saveSongWithAlbum(Song song, Long albumId)它内部会1检查专辑是否存在2生成唯一音频存储路径/songs/2024/06/1559979186433.mp33调用FileUtil.saveFile()把二进制流存到服务器指定目录4插入song表5更新album表的song_count字段6发送异步消息预留MQ扩展点。这六个步骤必须在一个事务里完成所以整个方法标注了Transactional(rollbackFor Exception.class)。如果第4步插入成功但第5步更新失败事务会自动回滚避免出现“歌曲入库了但专辑统计数没变”的脏数据。这种设计让后续加功能极其简单比如要增加“审核机制”只需在SongService.saveSong()里加一行song.setStatus(SongStatus.PENDING)再加个管理员审核接口完全不影响其他模块。DAO层数据搬运工采用MyBatis-Plus而非原生JDBC因为它的BaseMapperT能省掉80%的CRUD模板代码。所有Mapper接口都继承BaseMapperSongSongMapper.xml里只写复杂查询比如“根据关键词搜索歌曲并关联专辑信息”xml select idsearchSongsWithAlbum resultTypecom.example.music.dto.SongSearchDTO SELECT s.id, s.title, s.singer, s.duration, s.cover_url, a.name as album_name, a.id as album_id FROM song s LEFT JOIN album a ON s.album_id a.id WHERE s.status 1 AND (s.title LIKE CONCAT(%, #{keyword}, %) OR s.singer LIKE CONCAT(%, #{keyword}, %) OR a.name LIKE CONCAT(%, #{keyword}, %)) ORDER BY s.create_time DESC /select这里特意用了LEFT JOIN而非INNER JOIN因为有些歌曲可能未归属专辑album_id为NULL但搜索时仍需显示。同时WHERE条件里status 1过滤掉审核中的歌曲这是实际业务中必须的安全阀。工具与配置层隐形支柱config包里有MyBatisPlusConfig配置分页插件、SQL性能分析、WebMvcConfig统一静态资源映射把/songs/**指向服务器磁盘路径、CorsConfig解决跨域问题允许http://localhost:8080访问。特别提醒application.yml里数据库密码用ENC(XXXXX)加密配套jasypt-spring-boot-starter避免Git提交明文密码——这是学生项目最容易被导师扣分的安全漏洞。提示为什么不用Spring Data JPA因为JPA的Query写复杂联表查询时HQL语法和SQL差异大调试困难而MyBatis-Plus的XML写法更贴近DBA思维学生查资料、问ChatGPT都更容易上手。这不是技术优越论而是教学场景下的务实选择。2.2 前端Vue架构Vuex不是摆设路由不是跳转器而是状态协同的神经网络很多Vue教程教完vue-router就结束导致学生做出的项目点击首页→播放列表→再点首页播放器状态丢失、进度条归零。这套前端的设计哲学是——让状态流动像血液一样自然。整个前端结构清晰分为四块src/router/index.js路由中枢不仅定义路径更承担权限拦截和懒加载。比如用户未登录时访问/playlist路由守卫会重定向到/loginjavascript router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next({ path: /login, query: { redirect: to.fullPath } }) } else { next() } })同时所有页面组件都用动态导入component: () import(/views/Playlist.vue)打包时自动分割代码首屏加载更快。src/store/index.jsVuex总控室这里没有把所有状态塞进一个state对象而是按功能域拆分成模块modules/player.js,modules/user.js,modules/song.js。以player.js为例javascript const state { currentSongId: null, // 当前播放歌曲ID isPlaying: false, // 是否正在播放 currentTime: 0, // 当前播放进度秒 duration: 0, // 歌曲总时长秒 volume: 0.8, // 音量 playHistory: [] // 播放历史最多10条 }关键在于mutations的命名SET_CURRENT_SONG_ID、TOGGLE_PLAYING、UPDATE_CURRENT_TIME全部用大写下划线强制开发者思考“这个动作到底改变了什么”避免出现UPDATE_STATE这种模糊操作。actions里则封装异步逻辑比如loadSongDetail({ commit }, songId)会先调用API获取歌曲详情再提交多个mutation更新currentSongId、duration等。src/components/原子化UI组件不是按页面切分如HomeHeader.vue而是按功能切分。MusicPlayer.vue只负责渲染播放器UI和绑定事件不关心歌曲数据从哪来SongCard.vue只负责展示一首歌的卡片含封面、标题、歌手、收藏按钮数据通过props传入。这种设计让复用变得简单在搜索页、专辑页、收藏页只要传入不同songList数组就能复用同一个SongCard组件。src/assets/images/资源治理所有30张图片不是随意丢进文件夹而是按类型分三级目录assets/images/ ├── avatars/ # 用户头像tou.jpg, user.jpg ├── covers/ # 专辑封面L1.jpg, haidao.jpg ├── icons/ # UI图标play.png, pause.png, search.svg └── backgrounds/ # 背景图bg-music.jpg并且在main.js里全局注册了图片别名javascriptimport { createApp } from ‘vue’import App from ‘./App.vue’import ‘./assets/styles/common.css’const app createApp(App)// 全局注册图片别名避免硬编码路径app.config.globalProperties.$avatarPath ‘/images/avatars/’app.config.globalProperties.$coverPath ‘/images/covers/’ 这样在组件里写既安全又易维护。注意Vue版本锁定在2.6.14非3.x因为学校机房电脑普遍装的是老版Node.jsv14.xVue CLI 4.x对旧环境兼容性更好。如果你用Vue 3setup()语法糖和script setup写法虽新潮但调试时console.log(this)会输出Proxy对象对学生理解数据响应式反而造成障碍。3. 核心功能实现详解从“能用”到“好用”的细节打磨3.1 用户系统不只是注册登录而是安全与体验的平衡术学生项目最常犯的错误是把密码明文存数据库或者用MD5(password)这种弱哈希。本项目的用户认证体系兼顾教学演示性与基础安全性密码存储后端使用BCryptPasswordEncoderSpring Security提供它自动生成盐值并哈希同一密码每次加密结果不同。User实体类中密码字段定义为java TableField(fill FieldFill.INSERT) // MyBatis-Plus自动填充 private String password; // 存储BCrypt哈希值如 $2a$10$abc123...注册时UserService.register()方法会调用passwordEncoder.encode(rawPassword)生成哈希值再存库。登录时UserDetailsServiceImpl.loadUserByUsername()会用passwordEncoder.matches(inputPassword, dbHash)比对无需手动解密。Token机制放弃Session需要服务器内存存储采用无状态JWT。登录成功后后端生成JWTjava String token Jwts.builder() .setSubject(user.getUsername()) .claim(userId, user.getId()) .setExpiration(new Date(System.currentTimeMillis() 24 * 60 * 60 * 1000)) // 24小时 .signWith(SignatureAlgorithm.HS512, music-secret-key) .compact();前端将token存入localStorage后续所有请求在Header里带上Authorization: Bearer token。JwtAuthenticationFilter会拦截请求解析token并设置SecurityContextHolder让PreAuthorize(hasRole(USER))注解生效。这里music-secret-key是硬编码教学场景够用真实项目应从配置中心读取。前端登录流程Login.vue组件里表单提交触发this.$store.dispatch(user/login, formData)user.js模块的action会1. 调用API/api/auth/login2. 成功后将token存localStorage并提交mutationSET_TOKEN3. 调用getUserInfo()获取用户详情提交SET_USER_INFO4. 跳转到/home首页关键细节密码输入框用了typepassword但提交前会用正则校验强度至少8位含大小写字母和数字校验不通过直接return false避免无效请求。实操心得我在调试时发现Chrome浏览器对localStorage的setItem有大小限制约5MB如果把整个用户信息对象含头像base64全存进去会溢出。所以SET_USER_INFO只存必要字段id、username、avatarUrl头像图片依然走HTTP请求加载这是空间换时间的合理妥协。3.2 歌曲上传与播放解决“音频文件怎么存、怎么播”的终极方案这是音乐网站的核心痛点。学生常把MP3文件直接存数据库blob字段导致数据库膨胀、备份缓慢或把文件存在src/assets里打包后无法动态增删。本方案采用“数据库存路径文件存磁盘”的经典模式后端文件存储逻辑1.SongController.uploadSong()接收MultipartFile file2. 生成唯一文件名String fileName System.currentTimeMillis() . FilenameUtils.getExtension(file.getOriginalFilename());3. 确定存储路径String uploadDir /opt/music-server/songs/ LocalDate.now().getYear() / LocalDate.now().getMonthValue();4. 创建目录并保存FileUtil.saveFile(file.getBytes(), uploadDir, fileName)5. 将相对路径存入数据库song.setAudioUrl(/songs/ LocalDate.now().getYear() / LocalDate.now().getMonthValue() / fileName);前端播放器实现MusicPlayer.vue不依赖第三方库用原生audio标签html audio refaudioRef timeupdateonTimeUpdate endedonEnded loadedmetadataonLoadedMetadata source :srccurrentSong.audioUrl typeaudio/mpeg /audioloadedmetadata事件触发时获取audioRef.duration并提交UPDATE_DURATIONtimeupdate事件每250ms触发一次读取audioRef.currentTime并提交UPDATE_CURRENT_TIME拖拽进度条时调用audioRef.currentTime newTime并触发播放audioRef.play()关键优化点跨域音频后端WebMvcConfig里配置了registry.addResourceHandler(/songs/**).addResourceLocations(file:/opt/music-server/songs/)让/songs/2024/06/xxx.mp3请求能正确映射到磁盘文件。音频预加载audio preloadmetadata只预加载元数据时长、封面不加载全部音频流节省带宽。移动端适配iOS Safari要求用户手势触发播放所以在play()前加了try { audioRef.play() } catch(e) { console.log(iOS需用户点击) }并给播放按钮绑定clickhandlePlay确保有用户交互。注意事项Linux服务器上/opt/music-server/songs/目录权限必须是755且运行Java进程的用户如tomcat对该目录有写权限否则上传会报java.io.FileNotFoundException。部署指南里明确写了chown -R tomcat:tomcat /opt/music-server这点绝不能漏。3.3 搜索与收藏让数据关系“活”起来的SQL与Vuex协同搜索不是简单LIKE模糊匹配收藏不是前端改个图标。它们背后是数据库设计与前端状态管理的精密配合搜索功能后端SongMapper.xml的searchSongsWithAlbum查询已展示过但前端Search.vue的实现更讲究输入框加了v-model.lazysearchKeyword.lazy修饰符延迟到失焦才更新避免每敲一个字就发请求使用防抖watch: { searchKeyword: { handler(newVal) { this.debouncedSearch(newVal) }, immediate: false } }debouncedSearch是用lodash.debounce封装的函数延迟300ms执行搜索搜索结果高亮用v-html渲染后端API返回时把关键词用span classhighlight包裹如title: 海阔天空span classhighlight天空/span收藏功能这是体现“前后端分离”精髓的地方。用户点击收藏按钮前端不直接改UI而是1. 调用API/api/collection/toggle?songId1232. 后端CollectionService.toggleCollection(userId, songId)先查user_collection表是否存在该记录存在则删除取消收藏不存在则插入添加收藏3. API返回{ success: true, isCollected: true }4. 前端收到响应后提交mutationTOGGLE_COLLECTION_STATUS更新Vuex里songList中对应歌曲的isCollected字段5. 所有引用该歌曲的组件SongCard.vue,PlaylistItem.vue自动更新收藏图标这种设计确保了状态一致性即使用户在收藏页点了收藏再切到搜索页同一首歌的收藏状态仍是实心图标因为Vuex里songList是共享的单一数据源。实操心得我最初把收藏状态存在localStorage结果发现当用户在PC端收藏后手机端打开还是空心图标——因为localStorage是域名隔离的。改成Vuex全局状态API同步问题迎刃而解。这也印证了一个原则前端状态管理的边界应该由业务一致性决定而不是技术便利性。4. 部署与调试全流程从本地IDE启动到Linux服务器上线4.1 本地开发环境搭建三步到位拒绝“环境配置地狱”很多学生卡在第一步导入项目后IDE报红一片。本工程包的pom.xml和package.json已做极致简化后端IntelliJ IDEA1. 下载JDK 1.8必须SpringBoot 2.3.x不支持JDK 172. 打开项目根目录IDEA自动识别为Maven项目3. 点击右上角Maven面板 →Reload project等待依赖下载完成4. 修改application.yml里的数据库配置yaml spring: datasource: url: jdbc:mysql://localhost:3306/music_db?useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_mysql_password5. 运行MusicApplication.java主类控制台输出Started MusicApplication in X seconds即成功前端VS Code1. 确保Node.js版本≥14.17.0node -v查看2. 进入frontend目录注意压缩包里Mqzdzi46Nr9FweBW4SMv-master-...文件夹就是前端源码3. 执行npm install国内建议先npm config set registry https://registry.npmmirror.com4. 修改vue.config.js里的代理配置javascript devServer: { proxy: { /api: { target: http://localhost:8080, // 后端SpringBoot端口 changeOrigin: true, pathRewrite: { ^/api: /api } } } }5. 执行npm run serve浏览器打开http://localhost:8080即可访问提示如果IDEA启动报Caused by: java.lang.ClassNotFoundException: javax.servlet.Filter说明缺少Servlet API依赖。在pom.xml的spring-boot-starter-web依赖下添加exclusionsexclusiongroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-tomcat/artifactId/exclusion/exclusions再单独引入javax.servlet:javax.servlet-api:4.0.1。这是SpringBoot 2.3.x移除Tomcat嵌入式容器导致的兼容性问题。4.2 Linux服务器部署手把手教你绕过90%的坑部署不是复制粘贴命令而是理解每一步的目的。以下是CentOS 7上的完整流程Ubuntu类似步骤1安装基础环境bash# 安装Java 8OpenJDKsudo yum install java-1.8.0-openjdk-devel# 安装MySQL 5.7官方YUM源sudo rpm -Uvh https://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpmsudo yum install mysql-community-serversudo systemctl start mysqldsudo grep ‘temporary password’ /var/log/mysqld.log # 获取初始密码mysql_secure_installation # 设置root密码删除匿名用户等步骤2初始化数据库bash # 登录MySQL mysql -u root -p # 创建数据库UTF8MB4支持emoji CREATE DATABASE music_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 导入SQL脚本假设脚本在/root/music.sql source /root/music.sql; # 创建应用专用用户比root安全 CREATE USER music_applocalhost IDENTIFIED BY StrongPass123!; GRANT ALL PRIVILEGES ON music_db.* TO music_applocalhost; FLUSH PRIVILEGES;步骤3部署后端Jar包bash# 创建应用目录sudo mkdir -p /opt/music-server/{logs,config}# 上传jar包假设叫music-backend.jar到/opt/music-server/# 上传application-prod.yml到/opt/music-server/config/# 编写启动脚本 /opt/music-server/start.sh#!/bin/bashnohup java -Dspring.config.location/opt/music-server/config/application-prod.yml \-Dlogging.config/opt/music-server/config/logback-spring.xml \-jar /opt/music-server/music-backend.jar /opt/music-server/logs/app.log 21 echo $! /opt/music-server/app.pid# 赋予执行权限并启动chmod x /opt/music-server/start.sh/opt/music-server/start.sh步骤4部署前端静态文件bash# 安装Nginxsudo yum install nginx# 构建生产包在本地VS Code执行 npm run build生成dist目录# 将dist目录上传到服务器 /usr/share/nginx/html/music/# 修改Nginx配置 /etc/nginx/conf.d/music.confserver {listen 80;server_name your-domain.com;root /usr/share/nginx/html/music;index index.html;location /api { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /songs { alias /opt/music-server/songs; }}sudo nginx -t sudo systemctl restart nginx常见问题排查-问题Nginx访问首页正常但/api请求404排查检查location /api的proxy_pass末尾是否有/http://localhost:8080/会把/api/login变成http://localhost:8080//login双斜杠应写为http://localhost:8080-问题上传歌曲失败日志报Permission denied排查/opt/music-server/songs目录属主不是nginx用户执行sudo chown -R nginx:nginx /opt/music-server/songs-问题播放MP3时浏览器报CORS error排查后端CorsConfig已配置允许所有来源但Nginx反向代理时需显式透传Header在location /api块里添加add_header Access-Control-Allow-Origin *;5. 常见问题与避坑指南那些文档里不会写的血泪经验5.1 数据库相关高频问题问题现象根本原因解决方案启动报错Unknown column create_time in field listMySQL 5.7默认开启严格模式CREATE_TIME作为关键字被识别在application.yml的JDBC URL末尾添加sql_mode清空SQL模式jdbc:mysql://localhost:3306/music_db?useSSLfalseserverTimezoneAsia/Shanghaisql_mode搜索中文关键词无结果MySQL表字符集不是utf8mb4或连接URL未指定characterEncodingutf8创建数据库时用CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ciJDBC URL加characterEncodingutf8mb4收藏功能失效user_collection表无数据CollectionService.toggleCollection()方法未加Transactional插入失败后未回滚检查CollectionService类上是否有Service方法上是否有Transactional注解5.2 前端调试典型陷阱“页面空白控制台报错Cannot find module /components/SongCard”这不是路径错了而是Webpack别名未生效。检查vue.config.js里是否配置了javascript configureWebpack: { resolve: { alias: { : path.resolve(__dirname, src), } } }如果用了Vue CLI 4.x还需确认package.json里vue版本是^2.6.14更高版本可能破坏别名解析。“播放器能加载但进度条不动currentTime一直是0”这是audio标签的timeupdate事件未触发。原因通常是1音频文件URL跨域检查浏览器Network面板MP3请求是否标红2音频文件损坏用VLC播放器单独打开URL测试3audio标签未设置preloadmetadata导致元数据未加载。解决方案在MusicPlayer.vue的mounted()钩子里加this.$nextTick(() { this.$refs.audioRef.load() })强制加载。“移动端点击播放没反应iOS Safari提示‘NotAllowedError’”iOS策略要求必须由用户手势click/touch触发play()。常见错误是在created()或mounted()里直接调this.$refs.audioRef.play()。正确做法播放按钮的click事件处理器里调用play()且确保该按钮是可见的DOM元素不能是v-iffalse隐藏后又显示。5.3 部署阶段致命误区误区1“我把jar包放到服务器直接java -jar xxx.jar就完事了”错这样运行的进程会随SSH断开而终止。必须用nohup或systemd守护。更推荐systemd方式CentOS 7ini# /etc/systemd/system/music.service[Unit]DescriptionMusic Backend ServiceAfternetwork.target[Service]TypesimpleUsertomcatWorkingDirectory/opt/music-serverExecStart/usr/bin/java -jar /opt/music-server/music-backend.jarRestartalwaysRestartSec10[Install]WantedBymulti-user.target 然后sudo systemctl daemon-reload sudo systemctl enable music sudo systemctl start music误区2“Nginx配置好了但/songs/xxx.mp3返回404”这是因为Nginx的alias指令要求路径精确匹配。如果alias /opt/music-server/songs;那么请求/songs/2024/06/xxx.mp3会映射到/opt/music-server/songs/2024/06/xxx.mp3但如果alias /opt/music-server/songs/;末尾有斜杠则映射到/opt/music-server/songs//2024/06/xxx.mp3双斜杠导致失败。alias末尾不能加斜杠。误区3“我改了前端代码重新npm run build上传dist但浏览器还是旧页面”浏览器缓存了index.html。解决方案1在vue.config.js里配置configureWebpack.output.filename js/[name].[contenthash:8].js让JS文件名随内容变化2在Nginx配置里对HTML文件禁用缓存nginx location ~* \.html$ { add_header Cache-Control no-store, no-cache, must-revalidate, proxy-revalidate, max-age0; }最后分享一个小技巧当遇到诡异问题时比如明明改了代码却没生效不要急着查文档先执行三步1CtrlF5强制刷新2清除浏览器缓存CtrlShiftDel3在IDEA里File → Invalidate Caches and Restart。这三步能解决80%的“玄学问题”因为开发环境的缓存链路太长Webpack Dev Server → 浏览器 → IDE缓存任何一个环节卡住都会让你怀疑人生。本文还有配套的精品资源点击获取简介一个功能完整的在线音乐平台源码包后端用SpringBoot开发封装了用户注册登录、歌曲上传管理、播放列表创建、关键词搜索、歌曲收藏等常用接口前端基于Vue.js构建采用Vue Router做路由跳转Vuex管理全局状态界面适配PC和手机屏幕所有UI图标、用户头像、专辑封面等30多张图片资源已直接内置在项目中压缩包里包含前后端全部源代码已排除.git目录干扰文件、详细README说明文档、MySQL建表语句及初始化数据SQL文件项目结构遵循标准分层规范Controller/Service/DAOVue组件按功能模块划分清晰支持主流IDE如IntelliJ IDEA、VS Code一键导入本地启动调试方便也提供基础Linux服务器部署步骤说明适合计算机专业学生快速上手课程设计、实训项目或毕业设计。本文还有配套的精品资源点击获取