资讯动态

SpringBoot+Vue3+MyBatis前后端分离动漫网站项目实战全解析

发布时间:2026/10/14 4:04:15 来源:尧图企业网站定制
做国产动漫网站这个项目算是我把爱好和工作拧在一起的一次完整实践。项目标题拆开看就三块SpringBoot 负责后端接口Vue3 负责前端交互MyBatis 配 MySQL 处理数据一层一层分得很清楚标准的“前后端分离”结构。最终跑起来的效果是一个能完成动漫内容展示和用户交流闭环的站点——用户注册登录、按分类浏览番剧、搜索感兴趣的动画、查看详情、发评论、收藏内容管理员端则可以维护动漫数据、管理用户、审核评论。整套系统用来练手、做毕业设计、课程项目或者直接写进简历都非常能打。这篇文章我尽量不写废话直接把项目的需求怎么拆、技术选型怎么定、数据库怎么建模、后端怎么分层、前端怎么组织、部署踩了哪些坑一条一条理清楚。有基础的朋友可以直接照着复刻完全没有接触过前后端分离的新手也能顺着这个思路建立整体认知。1. 需求拆解与技术选型这座“动漫站”要解决什么问题1.1 功能清单用户端与管理端的分工拿到需求的第一件事不是急着写代码而是把“国产动漫网站”这六个字翻译成功能清单。我在设计时把系统分成了前台用户端和后台管理端两大块。前台面向普通用户核心动作是“找动漫、看信息、参与互动”后台面向管理员核心动作是“发内容、管用户、看数据”。这样划分之后整个项目的作用域一下就清楚了后续建表、写接口、拆页面全都有据可依。用户端这边我保留了最核心的几个入口。注册登录是第一道门用户信息存 MySQL密码用 BCrypt 加密登录后通过 JWT 签发 token前端每次请求带上它在拦截器里做身份校验。首页做的是内容聚合把热门番剧、最新更新、分类推荐几个区块拼在一起分类页支持按题材筛选比如热血、恋爱、科幻、日常搜索功能走的是标题模糊匹配。点进详情页之后用户能看到剧情简介、评分、连载状态、相关评论也可以执行收藏和评分操作。这些功能不贪多但把内容网站的典型路径完整串起来了。管理端的功能我一开始想做得很多后来还是做了减法。核心就三块内容管理可以新增、编辑、下架番剧上传封面图用户管理可以查看注册用户列表、把违规用户禁用评论审核可以对用户发表的评论进行前台隐藏或删除。这三个模块加上数据统计面板已经足够撑起后台驾驶舱的体验也不会给开发周期造成太大压力。做好减法之后整个系统的开发重心就集中在用户认证、动漫展示、评论互动这三条主线上代码量可控逻辑也清爽。1.2 技术栈决策为什么是这套组合技术选型其实是这个项目最不该纠结的部分。SpringBoot 的生态太成熟了starter 一拉配置一写接口开发速度非常快MyBatis 对复杂 SQL 的控制力强尤其适合动漫列表这种多条件组合查询的场景MySQL 做关系型存储用户、动漫、评论天然是表格结构用起来顺手。前端选 Vue3组合式 API 写业务逻辑比旧版本更清爽配合 Vite 在开发时热更新飞快。整个组合不花哨但胜在稳定、资料多、出问题容易排查。这套组合还有一个很实际的好处前后端可以并行开发。我先把接口文档定义清楚前端同学拿着 mock 数据就能开始写页面我这边同步把数据库表建好、把 Mapper 层写好。等到两边都差不多再进入联调阶段。相比传统后端渲染模板的方式前后端分离把部署也拆开了前端打包成静态文件扔到 Nginx后端打成一个 jar 包独立运行互不干扰。对于一个人开发这种毕业设计级别的项目来说这种并行节奏能省下大量时间。我在选型时也认真想过要不要加 Redis 做缓存、要不要上 Spring Security 做权限。后来都砍了。原因很简单项目规模撑不起那么多组件JWT 加拦截器已经能解决权限问题热门数据量也没到必须用缓存的级别。过度设计是新手最容易犯的错技术栈越复杂出问题的地方就越多先把主链路跑通再谈优化不迟。2. 数据库设计先行五张表撑起整个系统2.1 表结构详解从用户到动漫到评论数据库设计我花的时间最多因为表结构定不好后面写 Mapper 就是给自己挖坑。整套系统一共建了五张核心表用户表、动漫表、分类表、评论表、收藏表。用户表和动漫表是主体的两个大表评论表和收藏表分别是它们的关联扩展。这样的关系模型不算复杂但足够支撑核心功能。用户表 t_user 我放了 username、password、email、avatar、role、status 这些字段。username 建了唯一索引role 用字符串区分 USER 和 ADMINstatus 用来标记账号是否被禁用。密码字段存的是 BCrypt 加密后的密文长度要留够 60 位别只给 32 位否则加密结果存不进去。动漫表 t_anime 是内容的核心title、cover、category_id、status、rating、views、description、release_date 一个都不能少。其中 category_id 逻辑关联分类表用于做分类筛选rating 记录综合评分默认给一个初始值views 做播放量统计列表页按它排序就能实现“热门番剧”的排序逻辑。评论表 t_comment 则关联 anime_id 和 user_id保存 content 和 like_count为了把“发布时间”和“点赞数”这类附属信息都带出来。收藏表 t_favorite 用 user_id 加 anime_id 的唯一约束来防止重复收藏。建表 SQL 我直接放一段核心的其他表同理注意字符集统一用 utf8mb4否则存不了动漫作品名里的特殊符号和表情字符CREATE TABLE t_anime ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, cover VARCHAR(255), category_id INT NOT NULL, status TINYINT DEFAULT 1 COMMENT 1-连载 2-完结, rating DECIMAL(3,1) DEFAULT 8.0, views INT DEFAULT 0, description TEXT, release_date DATE, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;2.2 索引、字符集与外键取舍这里有个容易被新手忽略的点我没有建物理外键只保留逻辑关联。物理外键在数据量上去之后会拖慢写入性能而且删数据时触发限制很麻烦。靠代码层保证数据一致性配合索引更符合实际生产的习惯。user_id 和 anime_id 都要建索引因为评论查询、收藏查询基本都带着这两个条件走。这属于建表时顺手做的事后续查询效率差很多。另外时间字段我统一用 DATETIME不推荐用 TIMESTAMP。TIMESTAMP 有 2038 年问题而且会受时区影响DATETIME 存储更直观配合 Jackson 的格式化配置就能很好的输出给前端。如果你用的 MySQL 8.0驱动和时区配置也要注意后面常见问题里我会展开说。对于列表页的查询优化我建议避免在 WHERE 条件里对字段做函数运算比如把 WHERE YEAR(create_time)2024 改成范围查询不然索引会失效。动漫列表按 views 排序加 LIMIT 是很常见的分页查询这个场景下给 views 建一个普通索引就够了。数据量到十万级之前这套设计完全够用不需要引入分库分表。3. 后端开发实录SpringBoot 三层架构的落地细节3.1 项目分层与统一返回结构后端项目我采用的是经典三层结构Controller 层接收和校验请求Service 层处理业务逻辑Mapper 层与数据库交互。此外单独抽出了 entity、dto、vo、config、utils、common 几个包。实际编码中分层的作用不只是代码好看而是让每个类职责明确改一个功能不用把整个项目翻个底朝天。比如登录逻辑就只写在 Service 里Controller 不掺和密码校验和 token 生成。接口统一使用 /api 前缀返回结果封装成一个通用 Result 对象里面包含 code、message、data 三个字段。前端只要判断 code 是否为 200就知道请求处理成不成功。这样比后端抛异常、前端自己解析堆栈信息要干净得多。我贴上 Result 的核心代码这个类很轻但能统一全部接口的返回风格public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT r new Result(); r.setCode(200); r.setMessage(success); r.setData(data); return r; } public static T ResultT error(Integer code, String message) { ResultT r new Result(); r.setCode(code); r.setMessage(message); return r; } }业务异常也不要到处 try-catch。我写了一个全局异常处理器用 RestControllerAdvice 拦截自定义业务异常统一转成 Result.error 返回。这样 Service 里发现用户名已存在、评论内容为空这类情况直接 throw new BizException(用户名已存在) 就行代码读起来非常流畅不会满屏都是 try-catch 的噪音。3.2 注册登录与 JWT 认证闭环注册流程比较直白前端提交用户名、密码、邮箱后端先判断用户名是否已存在然后 BCrypt 加密入库。登录流程则是查库验证密码验证通过后签发 JWT。JWT 里我放了 userId 和 role过期时间设置为 24 小时。这里有个细节每次请求后端接口时拦截器去解析 Authorization 头里的 token解析失败直接返回 401。用户做收藏、发评论之前都必须过这一关。用户注册接口的 Service 层逻辑大概是先调用 Mapper 按用户名查用户查到了就抛异常没查到就创建实体类、加密密码、插入数据库。如果还想在注册时给用户初始化一个默认头像就需要把“插入用户表”和“插入默认头像记录”放在同一个事务里用 Transactional 注解保证两个操作要么同时成功要么同时回滚。事务是后端最容易忽略的细节尤其是多个写操作同时发生时不加事务哭都来不及。JWT 的实现不建议自己去手写签名算法直接用现成的 jjwt 库就行。生成 token 的核心代码就几行设置主题为 userId、设置过期时间、用密钥签名。拦截器的逻辑也不复杂取出 Authorization 头里的 Bearer token调用解析工具校验签名和过期时间解析出来的 userId 放进 ThreadLocal 或者请求上下文这样 Controller 里任何地方都能拿到当前登录用户的 ID。3.3 MyBatis 动态 SQL 与常见配置坑MyBatis 的使用上我倾向于简单 SQL 用注解动态条件较多的 SQL 用 XML。动漫列表页那种多条件筛选用 Select 注解拼字符串太难受XML 里写where标签配合 if 就很舒服。关键词、分类、状态三个条件都是可选的MyBatis 会自动组装出正确的 SQL。下面这段就是典型的动态查询写法select idselectAnimeList resultTypecom.example.entity.Anime SELECT * FROM t_anime where if testkeyword ! null and keyword ! AND title LIKE CONCAT(%, #{keyword}, %) /if if testcategoryId ! null AND category_id #{categoryId} /if if teststatus ! null AND status #{status} /if /where ORDER BY views DESC LIMIT #{offset}, #{pageSize} /select单是 Mapper 接口和 XML 文件名保持一致这点我见过很多新手踩坑。配置里要指定 mapper-locations比如 classpath:mapper/*.xml否则启动就会报 Invalid bound statement这个错误其实不是 SQL 写错而是 XML 没被扫描到。还有一个小细节如果 XML 里 comment 表名列是 create_time而实体类属性是 createTime一定要在配置里开启驼峰映射 map-underscore-to-camel-casetrue否则字段永远映射不上。4. 前端开发实录Vue3 组件化与前后端对接4.1 工程结构和路由设计Vue3 的项目我是用 Vite 初始化的比 Webpack 轻快很多。前端代码结构上src 下面分了 views、components、router、store、api、utils 几个目录。views 按页面维度组织首页 HomeView、分类页 CategoryView、详情页 DetailView、登录页 LoginView、后台 Dashboard 等components 放公共组件像 Header、Footer、AnimeCard、Pagination。路由用 Vue Router配置了 hash 模式这样部署到静态服务器时不容易出现刷新白屏的问题。路由的配置我分了两种普通用户路由和管理员路由。普通用户能访问首页、分类页、详情页、登录注册页管理员路由则统一加一个前置守卫判断当前用户角色。在路由守卫里每次跳转前检查 Pinia 里有没有 token没有 token 就跳转到登录页并且把 redirect 参数带上登录成功后可以跳回原来的页面。这种体验很多商业网站都在用实现起来其实也很简单。4.2 API 封装与 Pinia 状态管理登录状态我用 Pinia 来管理。Pinia 里定义一个 user store保存用户信息和 token。API 请求统一在 utils/request.js 里封装 axios 实例设置 baseURL、超时时间然后在请求拦截器里从 store 拿到 token 放到 header 的 Authorization 字段响应拦截器里统一判断 code如果是 401 就清空登录状态并跳回登录页。这样每个页面组件里不需要重复处理 token 和错误提示代码干净很多。axios 封装的核心代码就几行重点是拦截器的思路const service axios.create({ baseURL: /api, timeout: 10000 }); service.interceptors.request.use(config { const userStore useUserStore(); if (userStore.token) { config.headers.Authorization Bearer ${userStore.token}; } return config; }); service.interceptors.response.use( response { const res response.data; if (res.code ! 200) { ElMessage.error(res.message || 请求失败); return Promise.reject(new Error(res.message)); } return res.data; }, error { if (error.response error.response.status 401) { const userStore useUserStore(); userStore.clearUserInfo(); router.push(/login); } return Promise.reject(error); } );4.3 核心页面与组件复用首页是典型的组件拼装页面顶部 Header 负责导航和登录入口下面 Banner 轮播推荐位然后是热门番剧网格。卡片组件 AnimeCard 接收一个动漫对象展示封面、标题、评分点击跳转到详情页。详情页是最重的一个页面需要调动漫详情接口、评论列表接口还要处理收藏按钮和评分星星的交互。组合式 API 写起来很顺畅setup 语法里按逻辑分组读起来不费劲。页面之间传值我主要依靠路由参数比如列表页点击卡片路由跳转到 /detail/123详情页用 route.params.id 去请求数据。这种 URL 即状态的方案刷新后也不会丢体验比全局变量好。如果你想在列表页和详情页之间传递更复杂的对象可以考虑用 Pinia 存一份临时数据但我更推荐“能通过 id 查接口就绝不传对象”避免数据过期的问题。后台管理页面我用了一个简单的表格加表单组合。动漫列表用 el-table 展示点击编辑按钮弹出一个抽屉表单提交后调更新接口。这里要注意表单校验规则一定要写尤其是“标题不能为空”这种基础校验能挡掉大量脏数据。我最初没写校验结果测试的时候往数据库里插入了一堆空标题数据后来又花时间清理得不偿失。4.4 前后端联调的关键点跨域与代理联调阶段最容易炸的就是跨域。开发时前端跑在 5173 端口后端跑在 8080 端口直接 fetch 肯定被浏览器的同源策略拦下来。我不用在 axios 里写完整地址的办法而是在 Vite 的配置里加了 proxy把 /api 前缀的请求代理到后端地址。前端代码里统一写相对路径开发环境几乎感觉不到跨域的存在生产环境则交给 Nginxproxy_pass 一样能解决问题。我看过很多项目在开发环境把跨域处理得非常粗暴直接在 axios 里写 http://localhost:8080后端再用 CrossOrigin 放开。这种做法短时间内能跑通但到了上线阶段就麻烦改接口地址要把代码重新过一遍。用代理的方式只改一份配置前端代码里的请求路径始终保持一致强烈建议从一开始就这么做。5. 从本地到服务器部署流程与配置要点5.1 本地启动全流程在本地把整个项目跑通只需要几步装 JDK 8、Maven 3.6、Node 14、MySQL 5.7 或 8.0。先把项目里的 sql 脚本导入数据库然后改后端的 application.yml 里的数据库用户名密码在项目根目录执行 mvn spring-boot:run。前端 npm install 之后执行 npm run dev默认会起 5173 端口。整个流程不复杂但有几个细节要注意比如 MySQL 版本不同驱动依赖的写法也有差异。我用的 MySQL 8.0所以 pom.xml 里引入的 mysql-connector-java 版本要对应驱动的 class 也要写成 com.mysql.cj.jdbc.Driver。连接 URL 我习惯加上几个参数useSSLfalse、serverTimezoneAsia/Shanghai、characterEncodingutf8这几个参数看着不起眼少了任何一个是真会报错。数据库脚本导入时也要注意执行顺序先建 t_user、t_category再建 t_anime最后建评论和收藏表因为存在逻辑依赖关系。5.2 上线部署Nginx 反向代理与 Jar 包运维上线部署我推荐用 Nginx 托管前端打包产物后端 jar 包挂服务跑。前端执行 npm run buildVite 会生成 dist 目录把 dist 复制到 Nginx 的 html 目录下配置 location /api 反向代理到后端服务。后端的 jar 包直接用 java -jar 命令启动如果想更稳一点可以配合 systemd 做开机自启。整个过程只需要一台普通云服务器1核2G 就能扛得住。Nginx 配置我贴一份精简版核心就是两个 location一个把 /api 转发给后端一个处理前端路由的 history 回退server { listen 80; server_name your_domain; root /usr/share/nginx/html/dist; index index.html; location /api { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { try_files $uri $uri/ /index.html; } }5.3 敏感信息与环境变量我把数据库密码和 JWT 密钥写在了 application.yml 里便于本地演示。但如果真要上线至少要把这些敏感信息通过环境变量注入避免把密码提交到代码仓库里。这个小改动成本极低但很多人会忽略。SpringBoot 支持 ${DB_PASSWORD} 这种占位符写法启动命令里带上环境变量就行或者用一个单独的 application-prod.yml 覆盖默认配置。服务器上跑 Java 进程我习惯用 nohup 加重定向日志nohup java -jar anime-server.jar app.log 21 。日志是排查线上问题最重要的依据所以启动命令里一定不能省。平时改完代码重新部署就是先 kill 旧进程再启动新 jar配合 systemd 的话一条 systemctl restart 就搞定。部署流程简单出问题的概率才低。6. 常见问题排查实录与避坑手册6.1 高频问题速查表我把实际开发里碰到的高频问题整理成了一张速查表方便你对着排查问题现象可能原因解决方案后端启动报时区错误MySQL 8 连接 URL 缺少 serverTimezoneURL 加上 serverTimezoneAsia/Shanghai数据库驱动类找不到pom 里引入的依赖版本不匹配使用 mysql-connector-java 8.x驱动类写 com.mysql.cj.jdbc.DriverMyBatis 报 Invalid bound statementXML 文件没被扫描到检查 mapper-locations 配置和 MapperScan 注解后端返回时间是一串数字Jackson 没配置 LocalDateTime 序列化加 JsonFormat(pattern yyyy-MM-dd HH:mm:ss)前端上传图片报 413Nginx 和 SpringBoot 文件大小限制太严调大 client_max_body_size 和 multipart 配置刷新前端页面 404路由用了 history 模式但没配回退改为 hash 模式或 Nginx 加 try_files跨域请求被拦截前后端端口不一致开发环境用 Vite proxy生产环境用 Nginx proxy_pass这张表里的问题有八成都是配置问题而不是代码逻辑问题。遇到这些报错千万别急着改业务代码先检查配置项往往几分钟就能解决。我印象最深的是 MyBatis 那个绑定异常当时花了大半天把 Mapper 接口和 XML 翻了个遍最后发现只是 application.yml 里的 mapper-locations 写错了路径这种愚蠢的坑踩一次就长记性了。6.2 排查思路与个人习惯排查问题的顺序我个人习惯从外到内先看浏览器 Network 里请求到底发没发出去、状态码是多少再确认前端代理和后端路由是否匹配最后看后端控制台日志。大多数前后端分离的问题都死在前后端对接口字段的理解不一致比如后端返回的字段叫 userName前端写成 username一查一个准。所以接口文档一定要写在前面字段名、类型、是否必填都列清楚联调时会省很多事。这个项目做完我最大的感受是前后端分离的项目里真正值钱的部分不是某一个框架用得有多花哨而是把需求拆清楚、把表建稳定、把接口约定明白。现在回头看如果一上来就闷头写代码大概率会在联调阶段被跨域、字段名、事务问题耗掉大量时间。最后再分享一个扩展思路这个动漫网站的骨架完全可以复用换一换数据源、改一改页面样式就能变成影视站、小说站、游戏资讯站。数据库表结构和通用的用户、评论、后台管理模块基本不用动这是当初认真设计表结构带来的好处。

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

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

免费获取报价 →
↑