ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Spring Boot + Vue 前后端分离考研资讯系统设计与部署全流程

Spring Boot + Vue 前后端分离考研资讯系统设计与部署全流程 最近把一套基于 Spring Boot Vue 的考研资讯系统完整跑通了仓库代号是 cv6al6e3前后端代码加起来一万多行。这个项目不是那种看一眼就懂的 CRUD 演示它涵盖了内容资讯展示、院校专业库、在线课程播放、用户打卡、后台运营管理这些模块是一个比较典型的内容型前后端分离项目。我把它从数据库建模到前端路由、从视频播放到服务器部署完整走了一遍过程中踩了不少坑也把 Spring Boot 自动装配、Redis 缓存、JWT 认证、Vue 路由守卫、m3u8 视频播放这些高频考点全部串进了项目里。这篇文章我就按实际开发顺序把整套系统的设计思路、核心代码逻辑、部署方式和排错经验一次说清楚适合正在做毕设的计算机专业学生、想积累项目经验的初级开发者以及准备面试时想拿真实项目讲技术点的人参考。1. 考研资讯系统先想清楚要做什么再写代码很多同学拿到这类项目标题第一反应是“登录注册 增删改查”然后直接开写。等你写到一半就会发现考研资讯系统真正麻烦的不是用户表而是资讯内容怎么分类、院校数据怎么结构化、视频课程怎么播放、运营后台怎么管理内容。这些业务边界没想清楚代码写得越快返工越狠。1.1 业务拆解资讯、院校库、打卡、管理后台缺一不可我接手这个项目后先花了一天把需求拆成了四个大块这是整个项目最值得先做的事。第一块是资讯内容端。考研资讯不是简单发一篇文章它有多个栏目院校动态、政策解读、复习经验、调剂信息都混在一起的话用户根本找不到想要的内容。所以资讯表必须带栏目字段前端导航栏、首页推荐位、列表筛选全部依赖这个字段。资讯详情页还要有浏览量统计方便后台做热门推荐。第二块是院校与专业库。这是资讯系统区别于普通博客的核心。院校需要按省份、城市、层次985/211/双非、类型综合/理工/师范筛选专业要关联所属院系和考试科目。我在设计时单独建了学校表、专业表、学校专业关联表三张表没有把专业信息塞在学校表里否则后面做筛选和详情页的时候会非常痛苦。第三块是学习辅助功能。项目里有一个复习打卡模块用户每天记录学习科目和时间系统按周生成统计图表。这类功能技术难度不高但很能体现系统完整性放在项目描述里也是加分项。视频课程模块也是这一块的重点管理员上传 mp4 视频系统转成 m3u8 切片供前端播放这个后面单独讲。第四块是后台管理系统。资讯发布、院校数据维护、用户管理、打卡记录查询、视频上传管理这些操作全部走独立的管理员界面。前端用 Vue Router 做嵌套路由区分前台和后台后端通过用户角色字段配合拦截器控制接口访问权限。这样拆完之后技术选型就很清晰了数据关系明确、前后端交互频繁、有内容展示也有文件上传Spring Boot Vue 前后端分离正好是最稳妥的组合。1.2 技术选型Spring Boot Vue 前后端分离的匹配逻辑选 Spring Boot 做后端几乎没有悬念。它内嵌了 Tomcat不用额外部署容器打一个 jar 包就能跑自动装配机制帮你省掉大量 XML 配置生态里 MyBatis-Plus、Redis、JWT、EasyExcel 这些轮子都现成学生项目最需要的就是这种“能快速落地”的框架。前端选 Vue 是因为它对新手最友好。模板语法贴近 HTMLVue Router 做路由跳转非常直观Element Plus 组件库把表格、表单、弹窗、日期选择器这些后台管理界面常用的东西都封装好了。相比 React 全家桶Vue 的学习曲线要平缓得多。我这次选择的组合是 Spring Boot 2.7.x JDK 8 MyBatis-Plus Redis前端 Vue 3 Vite Element Plus Pinia。这里特别提醒一下版本问题现在新项目直接生成 Spring Boot 3.x 的话要求 JDK 17很多人的本机环境还是 JDK 8跑起来会直接报 class 版本错误。另外不少第三方 starter 对 Spring Boot 3 的兼容性更新不及时光排查依赖冲突就要耗掉半天时间。既然项目目的是把系统完整落地建议老老实实用 Spring Boot 2.7.x这也是目前企业里存量项目最多的版本线。1.3 开发环境准备IDEA 创建 Spring Boot 项目和 Vue 工程IDEA 创建 Spring Boot 项目时默认连的是 Spring 官方 Initializr网络不好的时候转圈很久才能出来。我建议在配置里把 Server URL 换成阿里云镜像https://start.aliyun.com创建速度快很多而且会给你配好国内源后续下载依赖也顺畅。Vue 工程这边我直接用了 Vite 创建npm create vitelatest kyzx-web -- --template vueNode 版本建议 16.14 以上我用的是 18配 Vite 5 没有问题。装依赖的时候如果报 ERESOLVE 错误通常是不问版本冲突可以用npm install --legacy-peer-deps绕过去但建议先看一眼是不是某些包版本过老。2. 后端落地的核心细节与 Spring Boot 原理延伸后端不是光把接口写出来就完事。代码结构、表设计、认证方案、缓存策略每一项都影响后面能不能继续扩展。这个项目做完之后我对 Spring Boot 的一些底层机制也有了更具体的理解下面按模块拆开讲。2.1 后端工程结构与分层设计我按常见的四层结构组织代码每个包职责单一后续维护的时候不会找不到文件。controller接收前端请求只做参数校验和结果封装。service业务逻辑层处理事务、调用多个 Mapper 组合数据。mapperMyBatis-Plus 的数据访问层复杂 SQL 写在 XML 里。entity数据库表对应的实体类和表字段一一对应。common统一返回结果、异常处理、工具类。configRedis、拦截器、跨域等配置类。dto/vo接口入参和返回给前端的数据对象。controller 里我统一返回ResultT结构格式是{ code, message, data }。前端 axios 拦截器里统一判断 code请求成功返回数据失败弹错误提示并跳登录。这样写的好处是接口风格统一前端处理逻辑极度简单不需要每个页面单独做异常判断。2.2 数据表设计别小看考研资讯这类内容型系统的建模数据表是整个系统的地基。我在设计时画清楚了每张表的关系核心表大概有这些表名用途关键字段说明user用户表username、password加密存储、nickname、avatar、rolearticle资讯文章表title、content、cover、category_id、status、view_countcategory栏目分类表name、sort资讯和视频共用school院校表name、province、city、level985/211/双非、typemajor专业表name、code、category、descriptionschool_major院校专业关联表school_id、major_id、exam_subjects、enrollment_countcourse课程表title、cover、video_url、m3u8_url、teacher、difficultystudy_checkin学习打卡表user_id、subject、study_date、study_minutes、notefavorite收藏表user_id、target_type文章/课程、target_idcomment评论表user_id、target_type、target_id、content、parent_id举个例子文章表单独建栏目分类表而不是直接写死字符串是因为资讯系统后面一定会扩展栏目写死字段会让你每次加栏目都要改代码。收藏表用target_type target_id这种多态设计可以一套逻辑同时支持文章和课程收藏不用为每种内容建一张收藏表。账号密码存数据库之前必须加密我用的 BCryptSpring Security 自带的密码编码器每次加密结果都不一样但校验方法能正确比对防止 数据库泄露后密码明文暴露。2.3 登录认证与权限JWT 拦截器方案为什么够用权限这块是面试必问的重头戏。这个项目我用了 JWT 拦截器没有上 Spring Security原因很简单系统的角色只有普通用户和管理员访问控制逻辑非常直接Spring Security 的过滤器链和配置复杂度对这个项目来说属于过度设计。等以后权限模型变复杂了再引入 Security 也来得及。JWT 的流程是用户登录成功后后端生成一个包含 userId 和 role 的 token返回给前端前端存储到 localStorage之后每次请求在请求头加Authorization: Bearer token。后端写一个AuthInterceptor拦截所有/api/**请求校验 token 签名、判断角色是否有权访问对应接口。public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (StringUtils.hasText(token) token.startsWith(Bearer )) { token token.substring(7); } try { Claims claims Jwts.parser() .setSigningKey(secretKey) .parseClaimsJws(token) .getBody(); request.setAttribute(userId, claims.get(userId)); request.setAttribute(role, claims.get(role)); return true; } catch (Exception e) { response.setStatus(401); response.getWriter().write({\code\:401,\message\:\未登录或token已过期\}); return false; } } }这里有几个细节容易踩坑。gateway 或 nginx 转发时请求头Authorization必须正确透传否则后端永远取不到 token 导致所有请求 401这个我在联调时排查了很久才发现是前端代理配置把请求头丢了。另外 JWT 是无状态的token 一旦签发只能等它过期所以我把过期时间设为 7 天管理员禁用用户的场景下需要结合 Redis 黑名单才能实现立即失效这个作为扩展点写在项目文档里就可以了。接口权限控制我用RequireRole(admin)注解在 controller 方法上拦截器通过反射检查方法注解判断是否放行。这种轻量方案在面试时很好讲你既能说出 JWT 原理又能说明白为什么不用复杂框架体现了你自己的工程判断力。2.4 Redis 缓存热点数据的几个实际问题考研资讯系统有一个非常典型的场景首页资讯列表、院校库列表这些数据是热点数据读的频率远大于写的频率如果每个用户打开首页都去查一次 MySQL数据库压力很大。我把这些列表缓存到了 Rediskey 是接口路径加查询参数value 是序列化后的 JSON过期时间设为 30 分钟。写缓存的时候要考虑三个经典问题缓存穿透是指请求一个 id 在数据库里根本不存在的数据缓存里自然也没有请求每次都打到数据库。我在缓存查不到时会把空值也缓存下来过期时间设短一点比如 5 分钟让恶意请求打不到数据库。缓存击穿是指某个 key 正好过期瞬间大量请求同时打到数据库。我用的是逻辑过期而不是物理过期查询时发现逻辑过期就加互斥锁去重建缓存其他请求先返回旧数据避免瞬间压垮数据库。缓存雪崩是指大量 key 在同一时间段过期。我把过期时间加随机值比如 30 分钟加 0 到 5 分钟的随机数避免大面积 key 同时失效。GetMapping(/list) public ResultListArticleVO list(RequestParam Integer categoryId) { String key article:list: categoryId; String cached redisTemplate.opsForValue().get(key); if (cached ! null) { return Result.success(JSON.parseArray(cached, ArticleVO.class)); } ListArticleVO list articleService.getByCategory(categoryId); if (list null || list.isEmpty()) { redisTemplate.opsForValue().set(key, [], 5, TimeUnit.MINUTES); } else { redisTemplate.opsForValue().set(key, JSON.toJSONString(list), Duration.ofMinutes(30).plusSeconds(new Random().nextInt(300))); } return Result.success(list); }面试时能讲清楚这三个问题比单纯说“我用了 Redis 缓存”要加分很多因为这说明你不只会调 API而是理解缓存系统在高并发下的实际风险。2.5 自动装配与 ConfigurationProperties 到底怎么运转这个项目里我定义了不少自定义配置比如 JWT 的私钥、token 过期时间、文件上传路径。我通过ConfigurationProperties把它们绑定到一个配置类而不是散落在一个个Value注解里。Component ConfigurationProperties(prefix jwt) public class JwtProperties { private String secret; private long expire; // getter / setter }对应application.yml里jwt: secret: your-secret-key expire: 604800000很多人面试被问“Spring Boot 自动装配原理”不知道从哪答。结合这个项目其实很好解释Spring Boot 的启动类上有SpringBootApplication它组合了EnableAutoConfiguration这个注解会通过AutoConfigurationImportSelector去读取依赖 jar 包里的META-INF/spring.factories或AutoConfiguration.imports文件把里面配置的自动配置类加载进来。每个自动配置类用ConditionalOnClass、ConditionalOnMissingBean这些条件注解控制只有满足条件才生效这就是为什么你引入spring-boot-starter-redis后只需要配置连接信息就能直接用 RedisTemplate因为 RedisAutoConfiguration 在类路径有 RedisTemplate 类时自动创建了相关 Bean。我碰到过一个真实问题自定义拦截器里Autowired注入的 Bean 一直为 null。原因是我手动 new 了拦截器对象没有由 Spring 容器管理。后来把拦截器做成Component在addInterceptors里从容器中获取问题解决。这个案例是理解 Spring 容器管理 Bean 生命周期的绝佳例子。3. Vue 侧开发实录从搭建到视频播放前端虽然看起来是“写页面”但涉及的环境搭建、路由设计、状态管理、媒体播放、接口联调每一步都有不少细节。我按实际推进顺序把 Vue 部分的经验完整过一遍。3.1 环境搭建Node 版本、Vite 还是 Vue CLI创建 Vue 项目我建议直接用 Vite这已经是社区的主流选择。Vue CLI 基于 Webpack冷启动速度和热更新体验都比 Vite 差不少。用 Vite 创建项目之后npm install然后npm run dev就能直接看到页面。安装依赖的时候遇到过不少问题。比如npm install中途卡住那是因为下载源访问不稳定用npm config set registry https://registry.npmmirror.com切换到国内镜像后速度快很多。还有一次 Element Plus 安装后组件样式全部不生效排查了半天发现是全局 CSS 文件引入了顺序不对Vite 构建时把业务样式覆盖了组件库样式解决办法是把 Element Plus 的基础样式放在main.js里最先引入。3.2 路由规划与权限守卫考研资讯系统的页面分为前台和后台两块我在 Vue Router 里用嵌套路由把它们组织起来const routes [ { path: /, component: HomeLayout, children: [ { path: , component: Home }, { path: articles/:id, component: ArticleDetail }, { path: schools, component: SchoolList }, { path: schools/:id, component: SchoolDetail }, { path: courses, component: CourseList }, { path: checkin, component: CheckIn, meta: { requiresAuth: true } } ]}, { path: /admin, component: AdminLayout, meta: { requiresAdmin: true }, children: [ { path: articles, component: AdminArticle }, { path: schools, component: AdminSchool }, { path: users, component: AdminUser }, { path: courses, component: AdminCourse } ]} ]路由参数传值这里有个容易混淆的点。/articles/:id这种是动态路由参数在组件里用this.$route.params.id读取如果跳转的时候带查询条件用this.$router.push({ path: /schools, query: { province: 北京 } })读取时用this.$route.query.province。两者的区别是params必须对应路由配置中的动态字段query会变成 URL 上的?keyvalue刷新后依然保留而刷新后 params 会丢失这是写路由时必须记牢的。路由守卫方面我配置了前置守卫做登录态判断和权限控制。没有 token 的用户访问打卡页面就跳转到登录页非管理员访问/admin直接踢回首页同时保留登录后回跳记录的参数这个细节比较友好router.beforeEach((to, from, next) { const token localStorage.getItem(token) const role localStorage.getItem(role) if (to.meta.requiresAuth !token) { next({ path: /login, query: { redirect: to.fullPath } }) } else if (to.meta.requiresAdmin role ! admin) { next(/) } else { next() } })3.3 Pinia 还是 Vuex状态管理选型项目里需要全局共享的状态不多主要是用户信息、登录态、购物车课程收藏这些。我选了 Pinia 而不是 Vuex理由是 Pinia 的 API 简洁得多没有mutations这个概念直接在 store 里定义 state 和 action而且天然支持 Composition API 和 TypeScript。// stores/user.js import { defineStore } from pinia export const useUserStore defineStore(user, { state: () ({ token: localStorage.getItem(token) || , userInfo: JSON.parse(localStorage.getItem(userInfo) || {}) }), actions: { setLogin(data) { this.token data.token this.userInfo data.userInfo localStorage.setItem(token, data.token) localStorage.setItem(userInfo, JSON.stringify(data.userInfo)) }, logout() { this.token this.userInfo {} localStorage.removeItem(token) localStorage.removeItem(userInfo) } } })面试如果问到 Pinia 和 Vuex 的区别我会结合项目说三点Pinia 去掉了 mutations异步操作直接在 actions 里写模块化不需要嵌套一个文件一个 store对 TypeScript 的推断更友好。这也是为什么新项目首选 Pinia 的原因。3.4 让页面支持播放 m3u8 视频视频课程是考研资讯系统的亮点功能这里要重点讲。我们的视频资源经过转码后生成 m3u8 索引文件和 .ts 视频切片m3u8 的本质是一个文本文件里面记录了视频分片的地址和时长播放器按顺序加载这些分片就能实现流畅播放同时支持拖动进度条时按需加载这在点播场景下能大幅节省流量。前端播放 m3u8 我用了hls.js兼容性很好Safari 浏览器自带对 HLS 的支持Chrome 和 Firefox 需要通过 hls.js 实现npm install hls.js播放器组件的核心逻辑template video refvideoRef controls playsinline stylewidth:100%/video /template script setup import Hls from hls.js import { onMounted, ref } from vue const props defineProps({ src: { type: String, required: true } }) const videoRef ref(null) onMounted(() { if (Hls.isSupported()) { const hls new Hls() hls.loadSource(props.src) hls.attachMedia(videoRef.value) } else if (videoRef.value.canPlayType(application/vnd.apple.mpegurl)) { videoRef.value.src props.src } }) /script实现播放本身不难但有两个坑必须提前说。第一是跨域问题m3u8 文件里的切片地址如果在另一个域名而那个服务器没有配置 CORS 响应头播放器会直接报跨域错误。除了视频服务器配置Access-Control-Allow-Origin也可以在 nginx 里对.ts和.m3u8请求统一加上跨域头。第二是要看 m3u8 里切片地址是相对路径还是绝对路径很多新手被这个问题卡住切片地址是相对路径时播放器会根据当前页面的域名去拼接结果自然是 404。最好是让后端在返回 m3u8 内容时把相对路径改写成完整的请求域名地址。视频上传这块项目里先用MultipartFile接收上传然后调用本地ffmpeg命令把 mp4 转成 hls 格式。实际做法可以简单一点直接用一行命令转换ffmpeg -i input.mp4 -codec copy -start_number 0 -hls_time 10 -hls_list_size 0 output.m3u8这是比较节省性能的方案。如果视频量大了可以再考虑用对象存储加转码服务的方案但对这个项目来说本地转码已经够了。3.5 封装 axios 与跨域处理前端所有请求我都封装在一个request.js模块里统一配置 baseURL、请求头、超时时间和响应拦截器。请求拦截器里自动加上 token响应拦截器里统一处理 401 和业务错误码。跨域问题是前后端分离项目绕不开的。开发环境最简单的方式是在 Vite 配置文件里开启代理export default defineConfig({ server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })这里changeOrigin: true必须配否则后端拿到的请求头里的 Host 还是前端地址如果后端做了域名校验就会出现请求被拒的情况。生产环境则通过 nginx 反向代理解决前端请求/api时自动转发到后端服务。这套逻辑在实际项目中通用前端代码里不用写死后端地址换环境只需要改配置。4. 打包、部署与线上注意事项系统本地跑通不算完能打包部署到服务器上正常运行才是完整的交付。这个环节踩坑最多的是前端静态资源路径和路由刷新 404 问题影响面很大必须单独梳理。4.1 前端打包常见的布局异常与刷新 404前端执行npm run build后生成的 dist 目录如果直接扔到 nginx 下访问常见情况是首页白屏、样式丢失、图片加载不出来而且刷新某个子页面时直接报 404。这些问题背后的原因完全是两码事我分别排查过的经验如下样式和图片丢失九成是publicPath的问题。Vite 默认的 base 是/意味着打包后资源引用路径是/assets/xxx.js如果你把项目部署在服务器根目录没问题但如果部署在子路径像http://ip:8080/kyzx/所有资源都会从根路径去找自然全部 404。解决办法是在vite.config.js里按部署路径设置 base。刷新 404 是另一个经典问题。Vue Router 如果用的是history模式路由路径是真实的 URL刷新时 nginx 收到这个路径发现服务器上并没有对应的静态文件就直接 404 了。有两种解决办法最直接的是 nginx 配置 try_files 把所有路径都回退到 index.htmllocation / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; }另一种是改用 hash 模式URL 会带#没有刷新 404 问题但不够美观。我用的 history 模式加 try_files视觉效果正常刷新也正常。我在部署时还遇到过一次 layout 异常的隐蔽问题Element Plus 组件库按需引入后打包出来样式顺序和本地开发不一致。后来查了原因把业务样式放在组件库样式之后引入问题解决。遇到布局异常别第一反应是改代码先确认是不是打包后资源路径和样式引入顺序的问题。4.2 Spring Boot 服务端打包与外部配置后端打包用 Maven 的package命令生成 jar 包。这里要注意跳过测试mvn clean package -DskipTests打包后生成的 jar 在target目录运行命令java -jar kyzx-server.jar --spring.profiles.activeprod我把配置拆成application-dev.yml和application-prod.yml。开发环境连接本地 MySQL 和 Redis生产环境通过环境变量注入数据库密码避免把真实密码写进代码仓库。这种方式在多环境切换时非常方便不会出现本地能跑、服务器上起不来的问题。端口设置我用的是 8080如果服务器上 8080 被占用了可以用--server.port8081指定。内嵌 Tomcat 的好处在这里体现得很明显不用像传统 Java Web 项目那样装独立 Tomcat 再丢 war 包一个 jar 一条命令服务就起来了。4.3 安全底线监控端点暴露与信息泄露部署上线阶段有一个安全点需要特别重视。如果项目里引入了 Spring Boot Actuator 做监控它有heapdump、env、beans这些端点默认情况下如果全部暴露攻击者可以直接把运行内存的 dump 文件下载下来再通过工具分析出配置信息和内存里的敏感数据这就是网上常说的 Spring Boot heapdump 敏感信息泄露漏洞。我在生产环境的配置里限制只暴露health和info端点management: endpoints: web: exposure: include: health,info另外所有需要登录的接口都经过拦截器校验但像 RSS 订阅、网站地图这些公开接口不需要登录注意绕过鉴权时只返回公开数据不要带用户相关信息。5. 开发中高频问题排查速查整个项目做下来真正花时间的不是功能开发而是排错。我把遇到过的典型问题整理成一个速查表每一条都是实际踩过的坑现象可能原因排查思路与解决办法前端请求后端接口报跨域错误后端未配置 CORS 或前端代理未生效开发环境检查 Vite proxy 是否生效生产环境检查 nginx 是否转发正确后端CorsConfig是否配置了允许的来源接口返回 401 但前端已登录token 过期、请求头没有带上 token 或 Authorization 头丢失查看浏览器网络面板确认请求头检查 axios 拦截器和网关代理配置上传大视频超时nginx 默认请求体大小限制 1MBnginx 配置client_max_body_size 200m;同时检查 Spring 的max-file-size配置m3u8 视频无法播放跨域、切片路径错误、hls.js 未正确挂载先手动打开 m3u8 地址看内容确认切片 URL 是相对还是绝对路径检查视频服务器的 CORS 头打包后首页白屏publicPath/base 路径配置错误打开浏览器控制台看资源加载地址对照实际部署路径修改 base刷新页面 404Vue Router history 模式未配置回退nginx 加try_files $uri $uri/ /index.html;Redis 启动后缓存数据没生效Redis 服务未启动或连接配置错误先redis-cli ping看是否 PONG再看 Spring Boot 启动日志有没有报连接失败Spring Boot 项目启动失败提示 ClassNotFound引入的依赖版本和 Spring Boot 版本不兼容优先用spring-boot-starter-parent管理版本第三方依赖查下兼容矩阵页面布局样式混乱组件库样式被业务样式覆盖、CSS 引入顺序错误检查 main.js 里的样式引入顺序把组件库样式放前面数据库插入数据中文乱码数据库连接未指定 UTF-8 编码jdbc url 加characterEncodingutf8检查数据库表字符集排查问题有一个总原则先分前端还是后端再定位到具体层级。前端打开浏览器开发者工具网络面板看请求状态码和响应内容后端看日志文件里的异常堆栈。遇到问题别盲目改代码先定位到最小复现路径改一处测一处效率最高。我记得有一次用户反馈登录后偶尔会跳到首页而不是上次访问的页面排查之后发现是登录接口返回数据里没有带redirect字段前端登录成功后就走了默认跳转。后面在前端登录页把跳转逻辑改成优先读route.query.redirect没有再出现过这个问题。这类小细节往往是面试时最有价值的素材因为它展示了你定位问题到解决的全过程。6. 从项目复盘到面试答题的沉淀一个完整项目做完之后如果不做深度复盘面试时很容易被问倒。Spring Boot 和 Vue 的面试题其实都可以拿这个项目当锚点来答让理论落到实际场景里。6.1 Spring Boot 高频问题可以怎么结合项目讲问“Spring Boot 自动装配原理”时我不会干背源码而是说我的项目里只加了spring-boot-starter-data-redis依赖配置文件里写了 Redis 地址项目启动后 RedisTemplate 就能直接用这是因为RedisAutoConfiguration这个自动配置类在类路径检测到 RedisTemplate 相关类后自动创建了 Bean内部通过ConditionalOnClass判断依赖是否存在通过ConditionalOnMissingBean保证用户可以覆盖默认配置。问“ConfigurationProperties 和 Value 的区别”时我拿 JwtProperties 配置类举例。Value 是逐个字段注入适合少量配置项ConfigurationProperties 是批量绑定一个配置前缀适合一组相关配置还能做类型校验和复杂类型绑定。我选择后者是因为 JWT 的私钥、过期时间、请求头名称这些配置是强关联的一组数据。问“Redis 在 Spring Boot 里的使用”时我直接说缓存热点资讯和登录 token 的场景然后展开缓存穿透、击穿、雪崩的解决策略面试官基本就能判断出你的水平在哪个档位。6.2 Vue 高频问题可以怎么结合项目讲问“Vue 响应式原理”时我结合项目里用户登录状态变化的场景说明。Vue 3 用 Proxy 拦截对象的读取和依赖收集修改属性时触发依赖更新所以 Pinia 的 state 变化能自动驱动页面刷新。问“路由守卫怎么用”时我讲述/admin管理后台和打卡页面的权限控制逻辑beforeEach 中检查 token 和角色未登录跳转登录页并带上redirect参数便于回跳。问“computed 和 watch 的区别”时我举了一个实际的例子在院校筛选页面我用 computed 根据用户选择的省份、层次、类型实时计算出筛选后的列表用 watch 监听搜索关键词的变化做防抖后请求后端搜索接口。computed 是依赖变化时自动重新计算有缓存watch 适合做异步操作或复杂业务逻辑。问“Vue 打包后布局异常”时我不仅说 publicPath 和顺序问题还会讲如何用浏览器开发者工具定位是 CSS 加载失败还是 JS 报错导致的白屏这个排查方法比答案本身更有价值。问“为什么选 Pinia 不用 Vuex”时我把 Pinia 去除 Mutation、天然支持 Composition API、TypeScript 友好这三点结合项目实际说一遍比背概念强很多。6.3 这个系统还能拓展成什么样项目做完了不等于没有优化空间。我在项目文档里写了三个后续方向也给想做扩展的同学一些参考。第一个方向是检索能力升级。目前的资讯搜索就是 SQL 的 LIKE 模糊查询数据量大了之后效率和准确性都不够。可以引入 HanLP 分词做中文分词再结合倒排索引或者 Elasticsearch 做全文检索让用户搜“双非逆袭”能匹配到更精准的内容。第二个方向是办公与协作能力。后台运营人员发布资讯前可以考虑接入 OnlyOffice 实现在线编辑文档预览这样编辑审稿流程直接在系统内完成。虽然这个集成有一定工程量但一旦做出来项目的完整度立刻提升一个档次。第三个方向是流程审核。如果资讯发布需要多级审核可以集成 Flowable 工作流引擎或者简单一点加一个audit_status字段配合管理员手动审核——后者虽然简单但也是实际可用的方案。流程引擎的扩展更适合作为后期学习方向真正投入时多做评估。可视化大屏也可以做把用户打卡数据、文章浏览量、院校热度做成大屏展示前后端都有成熟方案视觉冲击力强适合作品集展示。最后分享一套我自己的调试习惯项目做到最后我养成了一套调试习惯分享出来应该对做同类项目的人有直接帮助。第一后端启动时一定观察日志看到Started KyzxApplication in xx seconds才能确认启动成功。很多接口报错看下启动日志就能找到原因。第二前端遇到报错先定位是请求问题还是渲染问题打开开发者工具 Console 和 Network 两个面板对照看。第三测试接口时我用 Postman 建了开发环境和生产环境两套环境变量切换环境只需要改一个 baseURL不需要改代码。第四全站搜索或关键词搜索接口我都会把入参和出参打在日志里方便排查问题上线前再去掉冗余输出。做项目的过程本质上就是不断遇到问题、定位问题、解决问题的过程。这套考研资讯系统从数据库建模到前后端开发再到部署上线每一环都有值得记录的地方。希望这篇复盘能帮到正在做类似项目的人少走一些弯路。
返回列表