资讯动态

Spring Boot历史故事展播系统开发实践:从数据库设计到JWT认证全解析

发布时间:2026/10/4 14:14:10 来源:尧图企业网站定制
作为一个做过好几个Spring Boot毕设项目的过来人看到“中华历史故事展播系统”这个选题时我第一反应是这选题有戏。历史故事本身就自带内容优势不像图书管理、商品管理那些系统千篇一律数据有看头、界面有特色、功能也够典型。这个项目听起来像是个门户类应用但真正要做得出彩你得把故事分门别类、做用户收藏、做评论互动甚至做热门推荐这一套下来Spring Boot的后端功底基本就练全了。这篇文章我就以实际开干的视角把整个系统的设计思路、技术选型、数据库结构、核心功能实现、还有那些最容易翻车的细节从头到尾捋一遍。不管你是要交毕设、写课程设计还是单纯想拿Spring Boot练手这篇都能帮你少走不少弯路。1. 项目背景与需求拆解1.1 这个选题好在哪选题这件事我的原则一直是功能要经典内容要有差异化。图书管理、商品管理、班级考勤满屏都是答辩老师一看就没兴奋点。而历史故事展播系统功能上覆盖了信息展示类系统的所有标配——列表、详情、分类、搜索、收藏、评论技术上又不挑三拣四用Spring Boot那一整套熟得不能再熟的技术栈就能搞定。但内容上它天然有差异化你真的录入几十篇秦汉、三国、唐宋时期的故事把朝代、人物、成语典故这些标签做好系统一跑起来就有模有样答辩展示效果好很多。再说说“展播/展示”系统和传统CRUD系统的区别。CRUD系统重点在后台管理前台就是一个表格展播系统前台是门户按分类陈列、有详情页、有热度榜用户在页面上的行为多样化这对后端的要求就上来了你得处理分页、处理搜索、处理浏览量自增、处理用户收藏关系。所以我认为这个选题是比“XX管理系统”更有含金量、又不至于超出毕设范围的稳妥选择。1.2 核心功能清单我先把功能切成用户端和管理端两块来列这样后面建表、写接口都会清晰很多。用户端注册登录用户注册、登录、修改个人信息密码、昵称、头像首页故事墙按发布时间、浏览量、收藏数展示故事卡片分类浏览按朝代/类型秦汉、唐宋、人物、成语典故等筛选故事故事详情富文本内容展示、浏览量自增、收藏搜索按标题或关键词模糊搜索故事个人中心查看我收藏的故事、我发布的评论管理端故事管理新增、编辑、下架、删除故事分类管理分类的增删改查用户管理查看用户列表、启用/禁用账号评论管理查看评论、删除违规评论数据统计可选加分项故事总数、用户总数、今日浏览量这个功能清单说实话已经比一般的毕设题目丰富了一截但每个功能都是Spring Boot里的常规操作不会给自己挖坑。关键是功能之间有天然的关联关系论文里画E-R图、写数据流分析都有素材。下面表格把核心模块和对应技术点列一下方便后面照着做。功能模块核心操作涉及技术点用户注册登录注册、登录、用户信息维护BCrypt加密、JWT、拦截器故事展示列表分页、分类筛选、详情MyBatis Plus分页、QueryWrapper收藏功能添加/取消收藏、收藏列表多对多关系、唯一索引评论功能发表、查看、删除评论关联查询、敏感词过滤管理后台故事/分类/用户/评论管理角色权限、富文本编辑2. 技术选型与架构设计2.1 为什么选Spring Boot选Spring Boot这事其实不需要纠结。Spring Boot解决的核心问题就是“配置地狱”它通过自动配置把Spring、Spring MVC、MyBatis这些组件的初始化都替你安排好了你只要在pom.xml里引入starter依赖再在application.yml里写上数据源地址一个能跑的Web应用就起来了。对做毕设来说省掉的是大把的XML配置时间换来的是更快进入业务代码编写。有人说用SSMSpring Spring MVC MyBatis更“正统”其实Spring Boot底层用的还是Spring MVC和MyBatis那套东西只是把配置方式变成了约定优于配置。我的建议是就用Spring Boot 2.7.x版本因为2.7说明文档全、社区踩坑帖子多、兼容性好。Spring Boot 3.x虽然也出了但对JDK版本有要求如果你的开发环境是JDK 8那老老实实用2.7就行。要理解Spring Boot为什么“开箱即用”核心是SpringBootApplication这个注解——它组合了SpringBootConfiguration、EnableAutoConfiguration和ComponentScan。其中EnableAutoConfiguration是关键它会根据classpath下的jar包自动配置Bean。举个例子你引入了spring-boot-starter-web自动配置类就帮你创建DispatcherServlet和内置Tomcat引了mybatis-spring-boot-starter它就帮你创建SqlSessionFactory并扫描Mapper接口。这就是为什么项目能“拎包即跑”原理搞清楚了答辩被问到才有底气。2.2 前后端分离还是服务端渲染做类似展播系统你有两条路线可选。路线一传统模式用Thymeleaf模板引擎后端直接把HTML渲染好返回。路线二前后端分离后端只提供JSON接口前端用Vue独立开发。我当时选的是路线二。原因有三第一评委老师现在普遍认可前后端分离的项目形态答辩时可以说“前端Vue 后端Spring Boot通过RESTful API交互”话术上就多一个技术亮点第二Vue做故事卡片墙这种交互界面比Thymeleaf套页面灵活太多卡片布局、分类切换、动态渲染都很顺手第三把Vue打包后的dist目录放进Spring Boot的src/main/resources/static下部署的时候还是一个jar包跑起来并不复杂。但必须提醒一句如果时间紧、前端基础弱走Thymeleaf也完全没问题Spring Boot对模板引擎的支持非常成熟。别为了追求架构潮流把自己坑了毕设的完成度比技术栈的花哨程度重要得多。2.3 项目目录结构规划目录结构我直接给出一版我用下来最顺手的前后端分离项目的后端部分这么摆history-story/ ├── src/main/java/com/example/history/ │ ├── controller/ # 控制器层 │ ├── service/ # 业务层接口实现 │ ├── mapper/ # MyBatis Mapper接口 │ ├── entity/ # 数据库实体 │ ├── dto/ # 请求/响应对象 │ ├── config/ # 配置类拦截器、跨域、时间序列化 │ ├── common/ # 公共返回结果、统一异常处理 │ └── HistoryApplication.java ├── src/main/resources/ │ ├── mapper/ # MyBatis XML映射文件 │ ├── static/ # Vue打包后的静态资源 │ └── application.yml # 配置文件 └── pom.xmlController只管接收参数和返回结果Service里放业务逻辑Mapper访问数据库Entity对应数据表。这个分层是Spring Boot项目最经典的结构也是面试必问的分层思想——Controller不写SQLService里不堆SQLSQL都收在Mapper层。这么分的好处是后续改需求的时候你知道去哪一层改不会一个项目越写越乱成一锅粥。很多同学喜欢把所有逻辑全塞Controller里图一时爽快后面连自己都看不懂千万别这么干。3. 数据库设计与核心表结构3.1 五张核心表的设计这个系统的数据量不大五张表完全够用用户表、分类表、故事表、收藏表、评论表。如果你非要加个点赞功能可以再加一张点赞表或者直接在故事表加一个字段记录点赞次数就毕设的并发量来说根本不用担心数据一致性。下面是建表SQL可以直接拿去改。CREATE TABLE user ( id bigint NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 用户名, password varchar(100) NOT NULL COMMENT BCrypt密文, nickname varchar(50) DEFAULT NULL COMMENT 昵称, avatar varchar(255) DEFAULT NULL COMMENT 头像URL, role varchar(20) DEFAULT user COMMENT admin/user, status int DEFAULT 1 COMMENT 1启用 0禁用, create_time datetime DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE category ( id bigint NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL COMMENT 分类名, description varchar(255) DEFAULT NULL, sort int DEFAULT 0, create_time datetime DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE story ( id bigint NOT NULL AUTO_INCREMENT, category_id bigint NOT NULL, title varchar(100) NOT NULL, summary varchar(255) DEFAULT NULL COMMENT 列表摘要, content text COMMENT 富文本正文, cover varchar(255) DEFAULT NULL COMMENT 封面图URL, author varchar(50) DEFAULT NULL, view_count int DEFAULT 0, favorite_count int DEFAULT 0, status int DEFAULT 1 COMMENT 1上架 0下架, create_time datetime DEFAULT NULL, update_time datetime DEFAULT NULL, PRIMARY KEY (id), KEY idx_category (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE favorite ( id bigint NOT NULL AUTO_INCREMENT, user_id bigint NOT NULL, story_id bigint NOT NULL, create_time datetime DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_user_story (user_id, story_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE comment ( id bigint NOT NULL AUTO_INCREMENT, user_id bigint NOT NULL, story_id bigint NOT NULL, content varchar(500) NOT NULL, create_time datetime DEFAULT NULL, PRIMARY KEY (id), KEY idx_story (story_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;有几点设计上的考量要说明一下。第一user表里username做了唯一索引这是业务刚需注册时校验重复用户名不能只靠代码里查一遍数据库层的唯一约束才是最后一道防线。第二favorite表上的uk_user_story唯一索引解决的是同一用户不能重复收藏同一故事的问题这个索引一旦建好就算代码里漏了判断直接捕获DuplicateKeyException也能兜底。第三story表里summary字段不是摆设列表页如果用content全文去截取既慢又容易打破HTML结构所以最好在录入时单独写一句摘要。3.2 几个容易被忽视的设计细节第一user表的password为什么必须加密存储因为明文密码一旦数据库泄露就全完了。Spring Security里自带BCryptPasswordEncoder你哪怕没引入完整的Spring Security单独引入spring-security-crypto依赖也能直接使用BCryptPasswordEncoder.encode()和matches()方法。BCrypt的加密过程带随机盐同一密码每次加密结果都不一样但matches()方法能正确校验理解了这个机制答辩时被老师问密码安全就不慌。第二业务表加status字段是实现“逻辑删除/伪删除”的关键。物理删除数据风险很大万一删错了就找不回来。用status字段1表示上架/启用0表示下架/禁用。列表查询统一加条件status 1下架操作就是一条UPDATE不是DELETE。这类设计在生产环境非常常见答辩时主动提一句“我是用逻辑删除来保证数据可追溯”绝对是加分项。第三update_time字段一定要有。很多同学只建create_time等后面做数据统计分析、排查问题时才发现没有最后修改时间。MyBatis Plus里可以用TableField(fill FieldFill.INSERT_UPDATE)配合MetaObjectHandler自动填充省心且不会漏。3.3 初始化数据别敷衍数据库建好之后初始化数据是个大工程。我当时的做法是挑一个内容积累最丰富的方向——三国然后整理经典故事比如“三顾茅庐”“火烧赤壁”“草船借箭”每篇控制在800字左右配上简洁的摘要和一张封面图。封面图从公开素材网站找的统一裁成16:9的比例。这套“笨功夫”对最终效果提升极大。你可以想象答辩老师点开详情页看到的是排版干净、内容完整、来源可信的历史故事和看到“测试1”“测试2”的页面印象分天差地别。千万别用大数据课程实验那种“张三、李四”的假数据来糊弄系统和内容一旦看起来像玩具你后面讲什么技术亮点都会被打折。4. 核心功能实现实录4.1 注册登录与JWT认证注册接口的逻辑不复杂但细节不少。接收用户名和密码先trim掉用户名两侧空格再查重然后用BCryptPasswordEncoder加密密码存库最后设置默认角色和状态。密码校验规则前后端都要做长度8到20位且必须同时包含字母和数字。至于登录我强烈建议用JWT方案替代传统的Session拦截器。JWT的核心优势是后端不存登录状态登录成功返回一个token前端存起来后续请求在请求头里加上Authorization后端用拦截器校验就行。JWT相关依赖引入jjwt即可。生成token时把用户id、用户名和角色塞进payload过期时间设为7天。拦截器是核心Component public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行OPTIONS预检请求 if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } String token request.getHeader(Authorization); if (token null || !token.startsWith(Bearer )) { throw new BizException(401, 请先登录); } try { Claims claims Jwts.parser() .setSigningKey(jwtSecret) .parseClaimsJws(token.substring(7)) .getBody(); request.setAttribute(userId, claims.get(userId, Long.class)); request.setAttribute(role, claims.get(role, String.class)); return true; } catch (Exception e) { throw new BizException(401, 登录状态已过期请重新登录); } } }拦截器的路径配置要规划好/api/user/**、/api/favorite/**、/api/comment/**需要登录/api/story/list这种公开接口放行注册登录接口本身是放行的。这一步通过实现WebMvcConfigurer接口配置addInterceptors把excludePathPatterns列清楚。这里踩过的坑是拦截器里必须放行OPTIONS请求否则前后端联调时跨域预检会一直失败浏览器报错还不明不白。4.2 故事列表的分页与搜索列表接口是展播系统的门面用户打开首页就看到它。分页这块我用MyBatis Plus的IPage前端传pageNum和pageSize后端返回记录列表和总数。核心逻辑写出来大概是这样的Override public IPageStoryVO getStoryPage(int pageNum, int pageSize, Long categoryId, String keyword, String orderBy) { PageStory page new Page(pageNum, pageSize); QueryWrapperStory wrapper new QueryWrapper(); wrapper.eq(status, 1); if (categoryId ! null) { wrapper.eq(category_id, categoryId); } if (StringUtils.hasText(keyword)) { wrapper.and(w - w.like(title, keyword).or().like(summary, keyword)); } // 排序白名单 if (view.equals(orderBy)) { wrapper.orderByDesc(view_count); } else if (favorite.equals(orderBy)) { wrapper.orderByDesc(favorite_count); } else { wrapper.orderByDesc(create_time); } return storyMapper.selectPage(page, wrapper); }一个很重要的安全细节排序字段如果从前端直接传进来不能盲目拼进orderByDesc(变量)那会引入SQL注入风险。正确做法是白名单校验只允许view、favorite、time这几个预设值映射到固定的列名。这个习惯不只在毕设里有用以后进公司写接口也一样。还有个常见的坑是keyword为空时like条件不能带空串不然%%会扫全表数据量小的时候感觉不到量一上来就明显卡。所以上面用StringUtils.hasText(keyword)做了判空这个细节值得养成习惯。4.3 故事详情与浏览量统计详情接口返回故事的完整字段同时要查出当前用户是否收藏过该故事前端用这个字段控制收藏按钮的高亮状态。这里有个接口设计的经验详情接口和收藏状态查询可以合并成一个减少一次请求响应体里直接带favoriteStatus字段。浏览量统计这个功能我前后改了两版。最初是详情接口里每次访问就UPDATE story SET view_count view_count 1逻辑没错但实际操作下来发现两个问题第一前端组件在开发模式下会发重复请求导致浏览量虚高第二频繁的小更新语句虽然对数据库没啥实质压力但讨论到“高并发下怎么防刷量”的时候这个方案在答辩环节难以自圆其说。后来我改成“点击上报定时合并”的思路。前端只在用户点击故事卡片时调用一个热搜上报接口后端用一个ConcurrentHashMap把增量计数先暂存在内存里再用Spring Boot的Scheduled定时任务每60秒把缓存里的增量统一写入数据库Scheduled(cron 0 * * * * ?) public void flushViewCount() { if (viewCountMap.isEmpty()) { return; } for (Map.EntryLong, Integer entry : viewCountMap.entrySet()) { storyMapper.increaseViewCount(entry.getKey(), entry.getValue()); } viewCountMap.clear(); }这个方案不见得是生产级精确统计但思路是通的而且能自然引出“定时任务”“内存缓存”“批量写入”这些技术点。答辩的时候这一段是很好的“项目亮点”素材比单纯说“我用了一个count”强太多。顺带一提EnableScheduling别漏了漏了定时任务根本不会跑。这是我一个朋友踩过的坑他在Service里写好了Scheduled方法主启动类忘了加注解结果整个项目跑起来悄无声息查了半天才发现。4.4 收藏与评论功能收藏功能本质上是一个多对多关系的维护。用户收藏故事就是往favorite表插入一条记录同时故事表的favorite_count加1取消收藏则是删除记录favorite_count减1。这个操作要保证幂等重复点击收藏不报错而是直接返回当前状态。因为有3.1节里设计的(user_id, story_id)唯一索引插入前先查一次或者捕获DuplicateKeyException做兼容两种方式都可靠。更新favorite_count的时候直接用UPDATE story SET favorite_count favorite_count 1 WHERE id ?这种“数据库自增”的写法比先查出来再改安全不会出现并发下互相覆盖的问题。评论功能比收藏略复杂一些涉及分页。我做的是一级评论不做楼中楼毕设够用了。评论列表按故事id查询按创建时间升序返回分页处理。发表评论时后端不要信任前端传来的userId而是从拦截器解析好放在request属性里的用户id来取。这是从毕设起就该养成的习惯——后端永远不信任前端传来的身份信息否则别人传个1就能替你发评论了。5. 管理后台设计与实现5.1 管理端权限二次校验管理后台我是在同一个Vue项目里做路由级区分/login、/story这些是公开页面/admin/**是管理页面需要管理员角色才可见。但前端隐藏菜单不叫安全后端接口必须二次校验角色。实现思路是JWT的payload里带上role字段登录时写入。管理端再写一个AdminInterceptor专门拦截/api/admin/**路径校验request.getAttribute(role)必须是admin否则返回403。两个拦截器的执行顺序用order控制管理端拦截器在登录拦截器之后执行。这层“双重校验”不只是防御在答辩时也是一个完整的权限控制链路前端路由守卫 后端登录拦截 管理端角色拦截。三句话就能把权限方案讲清楚非常出效果。5.2 故事管理富文本编辑与封面图管理端最核心的模块就是故事管理。新增和编辑是一个表单标题、分类下拉框、简介、封面图URL、富文本正文。富文本编辑器前端用vue-quill-editor或wangEditor都可以这两个组件接入成本低输出HTML存库详情页再用v-html渲染出来。封面图这块需要单独说。毕设阶段不建议自己搭文件服务器我直接采用“本地磁盘存储 静态资源映射”的方案。写一个FileController接收MultipartFile保存到运行目录下的upload文件夹返回可访问的URL。但有个坑Spring Boot默认不会把磁盘上的文件映射为可访问的URL需要在配置类里加资源映射Configuration public class WebConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceLocations(file: System.getProperty(user.dir) /upload/); } }不加这段你文件存进去了但URL访问是404前端图片全部裂开别问我怎么知道的。5.3 评论管理与敏感词过滤评论管理模块我额外做了一个敏感词过滤工具类用前缀树Trie算法实现。整体思路是把敏感词列表逐字插入前缀树然后对评论字符串按字符遍历匹配命中就替换成*号。这个算法代码量不大但技术含量比普通字符串contains判断高好几个档次而且可以做高效的多模式匹配。具体来说评论发布人在提交评论时后端拿着评论内容过一遍过滤工具如果命中敏感词要么直接拒绝要么替换后存储。配合管理端的评论列表接口管理员也可以定期检查并删除不当评论。管理端的评论列表查询相对简单一条SQL联表查出评论内容、评论人昵称、故事标题按时间倒序分页展示。删除评论的时候如果评论属于可丢弃数据物理删除问题不大如果你想把删除记录也留下就再加个status字段做逻辑删除。我为了统一习惯全部走逻辑删除。6. 常见问题与排查技巧实录6.1 启动失败与端口占用Spring Boot项目启动时最常见的报错就是端口被占用日志里会出现Web server failed to start. Port 8080 was already in use.解决办法很简单在application.yml里改成server.port: 8081或者找出占用端口的进程并结束它。Windows下用netstat -ano | findstr 8080找到PID再用taskkill /pid xxx /f杀掉macOS/Linux下用lsof -i:8080再kill -9。还有一种启动失败是连不上数据库。控制台报Cannot create PoolableConnectionFactory这种就是看application.yml里的数据源三件套url、username、password。最常见的是密码错了或者MySQL字符集、时区设置问题url里加上?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai基本能解决大部分麻烦。6.2 分页查不出数据分页接口返回total是0记录集也是空的排查思路两步走。第一看参数。前端传的categoryId是字符串后端用Long接收时类型转换失败会得到null查出来自然没数据。用RequestParam时注意类型声明必要时加RequestParam(value categoryId, required false) Long categoryId。第二看条件。QueryWrapper里写的是数据库列名不是实体类的驼峰字段名。很多人在wrapper.eq(category_id, categoryId)这里写成了wrapper.eq(categoryId, categoryId)MyBatis Plus的mapUnderscoreToCamelCase只在实体映射时生效QueryWrapper的条件构造里用的仍然是数据库原始列名。这个细节特别容易漏查不出来会让人毫无头绪。6.3 跨域与预检请求前后端分离联调时后端接口常常被浏览器拦截报错信息是No Access-Control-Allow-Origin header is present。解决办法是添加全局跨域配置Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }这里有两个细节容易踩第一JWT方案里前端会在请求头带Authorization所以allowedHeaders要配*或显式包含Authorization第二跨域配置的执行顺序要优先于拦截器否则预检的OPTIONS请求会被拦截器拦下来跨域就失败了。所以拦截器里必须放行OPTIONS请求这点我在4.1节已经强调过两个配置要一起配合才稳妥。6.4 日期格式返回一串数字Spring Boot默认序列化LocalDateTime前端拿到的可能是一串时间戳数字或者类似2024-01-01T12:00:00的格式。调起来也简单在application.yml里配置全局Jackson格式spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8但这里有个隐藏坑这个配置只对java.util.Date生效如果是LocalDateTime还需要配合jackson-datatype-jsr310模块并做相应序列化配置或者在字段上加JsonFormat(pattern yyyy-MM-dd HH:mm:ss)。用MyBatis Plus的时候实体字段建议直接用LocalDateTime搭配全局配置里的spring.jackson设置能解决90%的展示问题。6.5 Vue打包放进Spring Boot后刷新404Vue项目用history路由模式时打包放进Spring Boot的static目录后访问首页没问题但直接刷新某个子路由页面比如/admin/story会404。原因很简单服务器上根本没有/admin/story这个物理文件请求被Spring Boot的默认404处理了。最简单的解法是前端改用hash模式URL上带个#号刷新不会发真实请求到服务端适合毕设场景省心不折腾。如果非要用history模式就要在Spring Boot侧做中间件规则或Controller转发把所有非/api、非静态资源的路径转发到index.html交给前端路由接管。但这个方案处理起来要小心资源路径所以我个人还是推荐毕设直接上hash模式美观和安全的取舍要看你的实际情况。写到最后的一些实在话这个项目做到最后我最大的体会是光会写CRUD不算本事能把每个环节的“为什么”讲明白才叫真懂。为什么密码要加密、为什么要用拦截器、为什么列表要分页、为什么收藏要建唯一索引、为什么要用逻辑删除这些看似基础的点恰恰是答辩时导师最爱追问的地方也是你写进论文“关键技术”章节的素材。如果你只是对着别人的代码抄一遍这些追问你大概率兜不住。还有一个建议想单独说别把时间全耗在折腾架构、换框架、调炫酷特效上。这个系统真正值钱的部分是内容整理能力和界面完成度。把20篇高质量的历史故事录进数据库把首页图片尺寸统一、卡片布局调和谐把详情页排版做干净这些“笨功夫”对最终效果的提升远大于花三天时间去研究什么高深技术。最后分享一个实用操作流程开发期间前端用Vue的devServer代理把/api请求转发到localhost:8080后端完全不用配跨域联调通过后再执行npm run build把dist目录里的文件复制到后端static目录下用浏览器完整走一遍用户流程和管理流程没问题就可以打包部署了。这套流程跑顺之后再做其他前后端分离项目你会非常得心应手。

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

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

免费获取报价 →
↑