资讯动态

Spring Boot+Vue个人博客系统:前后端分离开发全流程实战

发布时间:2026/10/15 4:26:48 来源:尧图企业网站定制
我见过太多人做个人博客要么用WordPress套个主题草草了事要么拿起Hexo写两篇就吃灰。真正想练手、想掌握前后端分离开发全流程的人最后还是绕不开Spring Boot加Vue这套组合。手头这套博客系统就是我当时从零搭起来、后来反复重构过的完整项目包含源码、数据库脚本和配套文档拿来学习、改造成自己的作品都挺好用。这套系统解决的核心痛点很实在前端Vue负责页面交互和渲染后端Spring Boot提供干净的接口服务两边通过JSON格式的数据沟通互不干扰。你不需要在页面里混写Java代码也不用担心模板引擎的各种限制功能拆得清清楚楚扩展起来特别顺手。尤其适合三类人一是刚学完Spring Boot基础、想找个完整项目练手的人二是正在做毕业设计、需要一个能讲清楚原理的系统的学生三是想搭一个真正属于自己的技术博客、又不想被现成框架绑死的开发者。接下来的内容我会先讲整体设计思路再拆解核心功能模块和数据库建模然后走一遍实操流程最后把开发中容易踩的坑都列出来。1. 内容整体设计与思路拆解1.1 为什么是Spring Boot Vue这个组合个人博客系统听上去功能不多无非是写文章、贴标签、挂评论但真要做得顺手、代码不烂尾技术选型得认真琢磨。我最早也考虑过直接用JSP加Servlet的传统玩法理论上也能跑通但写起来太累页面里混着Java代码改个样式得整个页面重新编译前端稍复杂一点就寸步难行。后来换成Thymeleaf模板引擎比JSP干净一些但本质上还是服务端渲染的路子页面交互全靠后台拼接好再返给浏览器遇到异步刷新、局部更新这些需求代码会变得很别扭。Spring Boot加Vue这套组合的好处在于前后端彻底分离各干各的活。后端只用Spring Boot写API接口接收请求、处理业务逻辑、返回JSON数据前端Vue负责页面展示、路由跳转、状态管理完全不用关心数据是怎么存进数据库的。两边通过HTTP协议通信定义好接口规范就能并行开发。我在实际开发中感受最深的一点是前端改交互逻辑时后端接口完全可以不动只调整页面组件和数据绑定就行效率提升特别明显。1.2 技术栈选型参考这套系统主流程用的技术栈是Spring Boot 2.7加Vue 2.6当时选这个版本组合主要考量稳定性和生态成熟度。Spring Boot 2.7还在维护期内依赖兼容性很好Vue 2.6虽然不如Vue 3新但Element UI组件库在这个版本下最顺手网上能查到的踩坑经验也最多。如果你现在从头搭选Spring Boot 3加Vue 3也完全没问题只是注意Spring Boot 3要求JDK 17以上MyBatis-Plus等相关依赖也要选对应新版本。后端持久层我用的是MyBatis-Plus选它而不是原生MyBatis最大的优点是内置了通用的增删改查方法单表操作几乎不用写SQL省下大量样板代码。代码生成器还能根据数据库表直接生成实体类、Mapper接口和Service层代码建好表之后几分钟就能把基础代码骨架拉起来。权限认证这块用JWT加拦截器实现登录成功后签发一个Token前端每次请求带上后端拦截器校验身份不用在服务端存Session做前后端分离项目这个方案是标配。前端部分只用了Vue生态里最核心的几个库Vue Router做页面路由Axios发HTTP请求Element UI提供现成的UI组件。状态管理方面这个项目规模用不上Vuex大动干戈非要用的话可以加但大部分页面之间只需要通过路由参数传值就够了。Markdown编辑器选择了mavon-editor它基于CodeMirror开发支持实时预览、图片上传各种扩展语法也比较齐全。还有一个细节容易被忽略——代码高亮用的highlight.js配合编辑器一起集成文章展示效果才能到位。2. 核心功能模块拆解与技术要点2.1 文章管理与Markdown渲染链路文章管理是整个系统的心脏需要想清楚的最核心问题是内容以什么形态存储。我最后采用的方案是数据库存Markdown源文本前端展示时再渲染成HTML。为什么这么设计因为Markdown源文本方便编辑、版本对比和迁移博客文章以后要是想换平台发布拿源文件直接能走而渲染成HTML则交给前端统一处理后端只需要把原始文本原封不动地传给前端就行。后端的文章接口只做两件事接收文章标题、分类、标签、内容等字段入库查询时按条件分页返回列表或详情。需要注意的一个坑是文章内容字段要用LongText类型对应MySQL的longtext不然Markdown文本长一点就存不进去SQL会直接报错。另外状态字段建议设计成草稿和已发布两种默认走草稿点发布时才把状态改成1这对博客写作场景很实用文章写到一半存草稿第二天接着改不用一股脑全公开。前端渲染Markdown的链路是mavon-editor加载文章内容后内部先把Markdown编译成HTML再交给highlight.js对代码块做高亮处理。这个过程中最容易出的问题是代码块里嵌套了HTML标签时被编辑器误解析显示错乱。我当时的处理方式是在markdown-it的配置参数里开启html选项同时用highlight.js做好代码块识别实测下来很多特殊符号都能正确显示。预览效果和最终文章详情页要保持一致这里建议文章详情页也直接用mavon-editor的只读模式渲染而不是自己写一套解析逻辑省心不少。2.2 分类标签体系与归档查询写博客的人应该都有这种体会文章一多没有分类和标签的话找以前的文章简直是大海捞针。这套系统里分类和标签是分开设计的语义上也有区别。分类是树形结构最典型的场景是技术、随笔、读书笔记这种大方向层级可控标签则是扁平结构可以很灵活比如一篇讲JWT认证的文章能同时挂上Spring Boot安全JWT好几个标签方便交叉检索。表结构上文章和分类是多对一关系文章表存分类ID字段文章和标签是多对多关系需要一张中间表article_tag关联。用MyBatis-Plus实现关联查询时我踩过一个坑如果直接关联查多对多数据Page分页插件统计总数会不准确因为分页是在SQL层面执行的而关联查询结果集的行数不等于文章数。我的解决方法是分开查先查文章列表分页数据再根据文章ID集合批量查标签然后在前端组装好再渲染这样分页统计就完全没问题了。归档功能是按时间维度组织的前端需要展示年份加月份的分组列表类似2025年1月3篇这种效果。实现方式可以后端直接按日期分组统计也可以前端拿全量文章列表自行分组。考虑到后端只需要一条group by日期字段的SQL我建议放在后端做一年或半年内博客文章量通常不大直接在文章列表接口加一个归档统计的聚合接口就行。2.3 评论系统的三层设计评论功能看上去就是一张表存留言但要把体验做好有三层设计需要搞定。第一层是数据存储评论表至少要包含文章ID、评论人昵称、邮箱、评论内容、父评论ID、评论时间、状态字段。其中父评论ID是用来支持楼层回复的用户A评论用户B回复A这条回复记录的父评论ID就指向A的评论ID。前端展示时先渲染所有顶级评论再递归找出每个顶级评论下的子评论形成楼中楼效果。第二层是防骚扰这个一般容易被忽略。评论如果不加审核垃圾广告很容易就刷进来。我的做法是状态字段支持待审核和已通过两种值新评论默认待审核只有管理后台审核通过后才在前端页面显示。发送评论的接口还加了简单的前置校验昵称和邮箱必填邮箱格式要正确评论区内容不能为空。心里有数就行不搞太复杂能挡住大部分无脑垃圾评论就够了。第三层是操作体验提交评论用异步请求成功后前端把新评论追加到列表里不用刷新页面。这里有个体验细节用户评论提交成功后最好立即清空输入框并给出成功提示而不是等着刷新之后看结果。还有一个细节是评论时间不能直接显示2025-01-15 15:30:00这么硬的时间字符串建议做一个简单的相对时间转换比如2分钟前、3天前阅读体验会好很多。2.4 后台管理的权限划分前台页面和后台管理共用一套Spring Boot接口但权限控制必须分开。登录用户分为管理员和普通用户两种角色管理后台所有接口都要求登录后访问而管理类操作发文章、删评论、管理分类标签只允许管理员角色执行。实现上用了两个自定义注解一个标记需要登录的接口另一个标记需要管理员权限的接口通过Spring AOP切面统一拦截处理。权限这块我在实际项目中吃过亏一开始图省事只做了登录校验没区分角色结果任何人注册后都能跑到后台发文章改设置。后来重构时把用户表加了角色字段用枚举类型存ADMIN和USER拦截器里判断角色不匹配就返回403状态码问题就彻底解决了。前端同步处理管理后台的路由在Vue Router中配置了meta属性记录需要的角色信息路由守卫里校验不通过直接跳转登录页。双端都做校验才能保证接口不被绕过前端隐藏按钮只是提升体验不能作为安全屏障。3. 项目结构设计与数据库建模3.1 后端包结构与分层规范后端工程按标准的三层结构来划分Controller层接收参数和返回结果Service层封装业务逻辑Mapper层对接数据库分包命名遵循统一规范就能保证后期好维护。我习惯的包结构是controller、service、mapper、entity、dto、vo、config、common、utils。entity里放数据库表映射的实体类dto放接收前端参数的传输对象vo放返回给前端的数据视图对象这样分层的好处是参数校验和响应结构不会跟数据库实体搅在一起。在写代码时有一个容易忽视的分层规范Controller层只做参数接收、调用Service、统一处理返回不写任何具体的业务逻辑复杂的业务处理都放在Service层。常见新手会犯的毛病是在Controller里直接new一个QueryWrapper拼查询条件做数据筛选看上去方便其实把业务逻辑散落到控制层了后面加需求改查询规则时要找半天代码在哪里。各层得各司其职这样出问题才能最快定位到具体层。统一的接口返回结构这一块我建议在一开始就定义好不然后期改起来很伤。我用的标准是code字段表示业务状态码20000表示成功其他数字表示各种业务异常message字段存放返回提示信息data字段存放业务数据。前端Axios封装了响应拦截器判断code不是20000就直接弹错误提示省去每个接口单独处理错误。分页查询的返回结果也是统一结构用自定义的PageResult对象包两层data是当前页的记录列表total是总记录数。3.2 前端路由与页面组织Vue前端工程结构pages目录下按功能模块划分了多个页面组件分别是首页、文章详情页、分类页、标签页、归档页、关于页、登录页、后台管理相关页面。路由配置和页面一一对应前台页面都走游客可以访问的路径后台管理的路由统一挂在/admin路径下并由路由守卫做登录和权限判断。首页比较典型的布局是顶部导航栏显示博客名称和菜单项左侧主体区域按时间倒序列出文章卡片右侧侧边栏放个人简介、文章分类、标签云、最新评论这些模块。文章详情页左侧是正文内容区底部是评论区域右侧是文章目录导航。这种布局在博客圈里用得很多用户浏览体验比较成熟不需要自己凭空设计交互。页面组件按单文件组件的方式来写模板、脚本、样式都放在同一个vue文件里方便定位和修改。Axios请求封装是前端一个很关键的基础模块我在utils/request.js中统一配置了baseURL和请求超时时间并在请求拦截器里把登录后的Token从localStorage取出加到请求头Authorization字段。响应拦截器统一处理后端返回的code状态码遇到登录过期就清掉本地登录信息并跳转到登录页。这样封装一次所有页面组件里只需要调用封装好的request方法不用重复处理各种异常分支。3.3 数据库表设计与字段说明数据库我用的是MySQL 8.0字符集统一设置成utf8mb4排序规则选择utf8mb4_general_ci。用utf8mb4而不是utf8是因为它完整支持emoji表情和生僻字的存储评论内容或者文章标题里偶尔会有人用特殊字符用utf8的话这种数据写入会直接报错。数据库名我定为blog_system主要包含5张核心表用户表、文章表、分类表、标签表、评论表另外还有一张文章标签关联表。文章表是最核心的一张表字段包括主键id、标题title、摘要summary、正文内容content、封面图cover_image、分类ID category_id、状态status、浏览量view_count、是否置顶is_top、创建时间create_time、更新时间update_time。摘要字段容易被忽略但很实用列表页直接展示它不用截取正文内容既省流量又能保证显示效果。浏览量字段直接存数字每次访问详情时加一简单有效等流量大了再考虑Redis缓存计数器。用户表字段包括主键id、用户名username、密码password、昵称nickname、邮箱email、头像avatar、角色role、创建时间create_time。密码存储明文是大忌我用的方案是BCrypt加密存储注册时加密登录时用BCrypt校验就算数据库泄露了密码原文也不会直接暴露。分类表和标签表结构相对简单分类表是主键加分类名、描述、排序号标签表是主键加标签名。中间表article_tag只需要两个外键字段和主键id即可。3.4 数据库初始化SQL脚本设计项目配套的数据库脚本文件包含全量建表语句和初始测试数据。执行方式分两种如果是全新环境直接执行完整的初始化脚本表和测试数据一口气搞定如果是给已有库升级或补数据可以单独执行增量脚本。初始化脚本里我会按顺序执行建库、建表、插入默认数据这样一把跑通不依赖顺序。建表语句要特别注意字段类型和索引的设计。文章表的content字段我前面提到必须用longtext标题和摘要用varchar类型并设置合适长度。索引方面文章表的分类ID创建普通索引状态字段也建索引因为后台列表查询经常按状态过滤文章表和时间相关的查询多用create_time做排序给这个字段建索引能明显加快列表查询速度。评论表的外键字段、文章标签关联表的两列都要建索引不然数据量上来之后关联查询会变得很慢。默认数据这部分我准备了管理员账号和几篇示例文章管理员账号初始密码经过BCrypt加密之后写入脚本并且我会在文档里醒目标注第一次登录后要尽快修改密码。示例文章主要用来填充页面展示效果覆盖了不同分类和标签。这样下载源码后执行脚本启动项目就能直接看到完整的前台效果和后台管理的数据操作。4. 实操过程与核心环节实现4.1 本地环境准备与启动流程实操第一步是准备开发环境。JDK需要1.8或以上版本我用的JDK 8最稳定MySQL用5.7或8.0都可以Node.js建议14以上版本低了npm安装依赖容易报错。后端用IDEA打开工程等待Maven下载依赖时用Navicat连接MySQL在Navicat里直接执行数据库初始化脚本建好库和表。前端用VS Code打开vue目录打开终端执行npm install安装依赖这个过程是全网最磨人的环节经常因为网络原因装到一半失败建议配置国内npm镜像源实测下载速度能快好几倍。后端启动要改的配置在application.yml文件里主要改数据库连接的用户名、密码、URL地址这三项。端口号默认配的8080前端开发服务器端口8081前后端联调时需要配置代理转发。前端项目根目录下的vue.config.js里配置了devServer.proxy把/api路径开头的请求都代理转发到http://localhost:8080这样前端页面里发的请求就不会出现跨域问题。前端工程启动命令是npm run serve启动后浏览器访问localhost:8081就能看到博客首页。后端启动很简单运行主类里的main方法控制台看到Tomcat started的日志就说明启动成功了。启动过程中最容易遇到的一个问题是数据库连不上表现为启动日志报Communications link failure或者Access denied。前者一般是MySQL没启动或端口不对后者是用户名密码或权限配置错误。逐一排查配置项基本上都能解决。Maven依赖如果下载不下来先确认IDEA的Maven settings配置文件是否指向了国内镜像仓库这个配置对了能省很多时间。4.2 后端核心接口开发示例文章分页列表接口是后台系统最常用的接口以它为例把实现思路串一遍。Controller层接收页码current、每页大小size、以及可选的关键字keyword参数调用Service层的分页方法。Service层构造MyBatis-Plus的LambdaQueryWrapper根据keyword非空判断来动态拼标题的模糊查询条件按置顶字段和创建时间排序然后调用Mapper层的selectPage方法。查询结果用PageResult对象封装好返回前端的统一格式。GetMapping(/article/list) public ResultPageResultArticleVO list(RequestParam(defaultValue 1) int current, RequestParam(defaultValue ) String keyword) { PageArticle page new Page(current, 5); LambdaQueryWrapperArticle wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.isNotBlank(keyword), Article::getTitle, keyword) // 非空才拼接条件 .orderByDesc(Article::getIsTop) // 置顶文章排前面 .orderByDesc(Article::getCreateTime); // 新文章在前 articleService.page(page, wrapper); return Result.success(PageResult.build(page)); }这段代码里的一个关键设计是wrapper.like方法的第一个布尔参数这个参数是false时该条查询条件自动不拼进SQL这样就能在一个方法里同时处理有关键字和无关键字两种情况代码简洁且安全。用LambdaQueryWrapper而不是字符串写字段名能避免字段名拼写错误在编译期才暴露的问题。文章详情接口也用类似方式按ID查单条记录如果查不到就返回业务异常信息。作者在后台编辑文章的时候接口接收的是一个ArticleDTODTO字段比实体类多一些比如标签ID列表和分类ID都要通过DTO传入。创建文章时要先插入主表记录拿到文章ID再循环插入文章标签关联数据。这里要注意的是在循环里逐条insert单条数据性能尚可但如果标签非常多可以用MyBatis-Plus的批量插入方法一次搞定。4.3 前端页面关键实现前端列表页的核心逻辑是调用后端分页接口获取文章数据渲染到页面上。这段逻辑放在mounted生命周期里执行页面初始加载时调用一次loadArticles方法方法内部通过this.$http.get请求接口拿到数据后更新页面里的文章数组和分页组件当前页数。分页组件的事件绑定到handlePageChange方法页码变化时更新当前页码参数再重新请求接口。这里有一个小细节列表页的加载状态要处理好请求发出后显示loading遮罩数据返回后关掉体验上不会让人觉得点一下没反应。文章详情页除了加载文章内容还要处理Markdown渲染、浏览量加一、加载评论列表这三个并行任务。Markdown渲染这部分我直接用的封装好的mavon-editor组件设置只读模式把Markdown源文本绑定进去组件内部自动渲染。浏览量加一调的是后端单独的接口每次进入详情页时请求一次。评论列表加载放在页面底部用户登录状态下还能看到评论输入框和提交按钮。getArticleDetail(id) { this.$http.get(/article/detail/${id}).then(res { const data res.data; this.article data; // 文章加载完成后再渲染评论列表 this.getComments(id); }) }编写前端组件时我有一条实用建议凡是页面里需要的格式化逻辑比如时间转换、浏览量大于一万显示成w单位都抽到utils公共方法里不要在组件里临时写一遍。多个页面复用同一个格式化工具时以后要改格式只动公共方法一个地方不用满项目去找散落的代码。4.4 文章发布与管理后台流程管理后台的编辑器页面与前台的阅读体验是不同的需要用mavon-editor的编辑模式左边写Markdown右边实时预览效果。发布文章时填写的字段有标题、分类、标签、封面图、摘要、正文内容。标签字段用Element UI的input-tag组件处理用逗号分隔输入多个标签保存前转换成标签ID列表传给后端。编辑已有文章和新建文章的逻辑都复用了同一个编辑页区别只是在路由参数里多一个文章ID。如果带ID进来就先把文章详情查询出来填充到表单里再把关联的标签ID列表转换成标签名数组回显到tag组件。保存时根据是否有ID决定走新增还是更新接口。这种复用思路在后台系统的各个模块里普遍适用能减少大量重复代码。管理后台的整体布局采用左侧菜单加右侧内容区域的经典结构左侧菜单按功能分组文章管理、分类管理、标签管理、评论管理、系统设置。文章列表页提供按标题搜索、按分类筛选、按状态筛选三个条件操作列提供编辑、置顶/取消置顶、删除三个按钮。所有列表操作都加了一个二次确认弹窗防止手滑点错造成数据误删这个习惯强烈建议保持尤其是删除操作误删几篇文章的心痛谁都懂。5. 常见问题与排查技巧实录5.1 跨域请求与代理配置的坑前后端分离开发第一个绕不开的问题就是跨域。浏览器同源策略会拦截不同端口之间的Ajax请求前端端口8081请求后端8080端口默认情况下必然被拦。解决方式有两种第一种是前端配置代理第二种是后端开启跨域支持。我把两种都配了因为开发环境和生产环境遇到的场景不一样开发环境靠前端devServer代理转发生产环境部署在同域下就不存在跨域问题但为了保险还是给后端写了允许跨域的配置类。后端开启跨域支持写一个配置类实现WebMvcConfigurer接口重写addCorsMappings方法允许所有的请求路径跨域访问。需要注意allowedOriginPatterns和allowedOrigins的区别后者不允许携带通配符如果需求里要求允许所有来源访问用allowedOriginPatterns()而不是allowedOrigins()不然实际请求时会被浏览器拦截掉这个坑我当时排查了挺久才定位到。Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); }还有一种容易出现的情况是前端代理配好了但请求的URL路径没带/api前缀导致代理规则没生效。我在代理规则里专门加了一条对/api路径的匹配然后前端所有接口请求都封装在utils/request.js里baseURL统一写成/api前缀下的地址从根上避免这个问题。建议你也采用这种方式统一管理请求前缀。5.2 Token过期与登录态失效的处理JWT Token的有效期、前端登录态管理是一个需要注意设计的细节。Token过期后用户再操作后台某接口后端拦截器直接返回未认证的状态码。如果前端不及时处理用户会停留在页面上点了按钮没反应很困惑。我前端的做法是在响应拦截器里统一判断HTTP状态码为401时清空localStorage里的Token数据弹出登录过期提示然后跳转到登录页。这里要注意一个是Token过期后用户正在编辑的文章内容直接跳转登录页会把内容丢光体验很差。我踩过这个坑后调整了策略跳转登录页前先把当前页面的路由记录到sessionStorage里登录成功后再自动跳回原来的页面。这样一个简单的优化能让用户觉得系统很人性化细节决定体验。Token里我还加入了签发时间字段前端在操作敏感接口时如果发现Token快过期了可以提前提醒用户重新登录避免写了一半突然被打断。还有一个容易被忽视的细节是登录接口返回的Token要分清楚放哪个存储localStorage是持久化的关掉浏览器再打开还在sessionStorage是会话级的浏览器关掉就没了。个人博客系统里设置记住登录的就放localStorage没设置的就放sessionStorage。但要注意XSS攻击可以读取localStorage里的Token所以防止XSS和Token过期检测要一起做前端不要用v-html直接渲染不可信的用户输入内容后端在保存评论和用户资料时也要过滤掉危险的HTML标签。5.3 图片上传与存储策略文章内容里配图几乎是必备的小到封面图片大到正文里的截图演示都需要一个可靠的图片上传方案。我一开始用本地存储定义一个upload目录上传接口把文件保存到磁盘访问时通过静态资源映射返回。这个方案在开发环境没问题但部署到服务器后文件路径对不上、磁盘空间不足、备份困难这些麻烦事一件接一件。后来我把上传目标改为云存储用对象存储服务保存图片上传成功后返回一个外链地址存入文章字段或封面图字段。对象存储的好处是容量基本不用考虑访问速度快支持CDN加速而且不用担心服务器重启丢失文件。整体改造的思路是后端上传接口统一返回URL字符串前端拿到URL后直接填入图片地址字段不需要感知后端的存储细节。如果你不想引入云服务用Nginx静态文件目录存图片也是可行方案但要做好目录权限控制和容量预警。图片上传还有一个安全点要关注后端必须校验上传文件的类型和大小。只允许图片扩展名jpg、png、gif、webp文件大小限制在5MB以内最好再用文件头的magic number校验文件真实类型防止有人把木马文件伪装成图片上传到服务器上。这块实现不复杂但能帮你避免很多安全问题值得认真做。5.4 调试技巧与性能优化想起排查接口问题时一个提升效率的小技巧后端接口开发和联调阶段F12打开浏览器开发者工具在Network标签页观察接口请求的状态码和响应时间很快就能判断出问题是出在前端、后端还是网络层。看到接口200但页面没数据多半是前端渲染逻辑问题看到不返回数据往往是后端拼接数据逻辑有bug再打开控制台看打印的SQL语句和报错信息就行。如果接口数据正常但页面展示慢大概率是前端渲染性能问题肉眼看不出来就加console.time打点逐个排查数据链路每个环节的耗时。性能优化方面个人博客的量级一般不会太大主要做三个优化就够用。第一个是数据库查询优化列表页和详情页的SQL尽量走索引不用select *只查需要的字段。第二个是前端懒加载路由配置里用import函数动态引入页面组件首屏只加载当前页面需要的JS而不是一次性打包全部页面代码首屏加载速度会有明显改善。第三个是静态资源走缓存给Nginx配置上图片和静态JS/CSS的缓存策略。Web服务器部署这块我把前端build产物放在Nginx的html目录下后端打成jar包用systemd守护运行Nginx做反向代理把/api请求转发到本地8080端口。这样配置的好处是前后端处于同一个域名下自然不存在跨域问题同时Nginx自带静态文件性能优化能力用起来很省心。6. 项目扩展与后续开发建议博客系统写到能用只是第一步真正让它成为你自己的作品还需要结合使用场景做扩展。先说我个人最推荐加的模块友情链接管理这个功能技术上就是一张表加增删改查但能让博客和同行站点互相引流社区氛围一下就出来了。其次是网站公告或者公告栏组件发布重要信息时不用改代码后台填条数据就能生效。还有一个经常被人忽视的扩展点是搜索引擎优化常见做法包括给文章详情页动态生成title和description元信息、文章URL使用友好的拼音或ID、生成sitemap.xml文件提交给搜索引擎收录。这套系统做的过程中我给这些细节都预留了字段位置比如每篇文章可以填独立的SEO关键字后续要做的时候不用改表结构直接补逻辑就行。技术层面如果你想升级体验可以考虑接入Redis做热点数据的缓存和浏览量计数器流量起来之后能明显减轻数据库压力搜索功能可以接入搜索引擎中间件或者直接用数据库的全文索引都比在代码里遍历查询like要高效。前端可以考虑升级到Vue 3的组合式API组件复用逻辑更清晰但重构成本会比较高建议想清楚再动手。这套项目断断续续维护了差不多一年最大的感受是功能做着做着就会自然生长从最早只有最基础的文章发布到后来加了评论审核、标签聚合、归档统计、图片上传每一步都是使用中真实遇到的需求。做个人博客最珍贵的地方在于它逼着你把全栈链路都摸一遍从数据库设计到后端接口再到前端交互每一个环节出了问题都得自己解决。这个过程中踩过的坑、总结的思路比项目本身更值钱。以此为基础你可以任意改造成自己风格的博客系统换主题、加功能、改逻辑都由你说了算。开发中遇到问题建议先打开配套文档查常见问题部分再回到源码里对照排查很多时候思路就通了。

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

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

免费获取报价 →
↑