Spring Boot+Vue 3构建个人博客系统:从零到部署的完整实践指南

📅 2026/7/21 12:08:57
Spring Boot+Vue 3构建个人博客系统:从零到部署的完整实践指南
很多Java开发者尤其是学生和求职者都有一个共同的困惑简历上的“项目经验”一栏除了课程设计还能写点什么有分量的东西直接写个“图书管理系统”或“学生选课系统”在面试官眼里可能已经缺乏新意。但如果你能展示一个功能完整、技术栈主流、并且真正部署上线的个人博客系统情况就完全不同了。这个项目之所以成为“毕设简历必备”不是因为它技术多高深而是因为它精准地踩中了几个关键点它用Spring Boot和Vue 3这两个最主流的技术栈串联了从前端到后端、从数据库设计到部署上线的完整流程并且最终产出一个“看得见、摸得着”的线上作品。对于面试官而言这比任何空洞的描述都更有说服力。本文将手把手带你从零开始用Spring Boot Vue 3构建一个功能完整的博客管理系统。我们不止于“跑通代码”更会深入讲解为什么这样设计、实际开发中会遇到哪些坑以及如何将这个项目打磨成你简历上的亮点。你将获得一套可直接运行的源码并理解其背后的工程化思考。1. 项目核心价值为什么是个人博客在开始敲代码之前我们需要先想清楚为什么个人博客项目如此受青睐它解决的远不止“写文章”这个表面需求。1.1 技术栈的绝佳练兵场一个博客系统麻雀虽小五脏俱全。它天然涵盖了Web开发的绝大多数核心场景后端Spring BootRESTful API设计、用户认证与授权JWT、数据库CRUDMyBatis-Plus/Spring Data JPA、文件上传、缓存、事务管理、全局异常处理。前端Vue 3组件化开发、路由管理Vue Router、状态管理Pinia、异步请求Axios、富文本编辑器集成、响应式布局。前后端交互清晰的接口契约、跨域问题解决、请求拦截与响应处理。工程化Maven/Gradle构建、前后端分离部署、简单的CI/CD思路。通过完成这个项目你能系统性地实践一套现代Web应用的标准开发流程。1.2 面试话题的天然素材库这个项目能衍生出无数高质量的面试问题“你的博客如何实现用户登录状态保持” → 引出JWT vs Session的讨论。“文章列表分页是怎么做的” → 考察数据库分页优化深度分页问题。“富文本内容如何安全存储和展示” → 涉及XSS攻击防护。“前端是如何调用后端接口的” → 考察Axios封装、请求拦截器。“项目是如何部署的” → 引出Docker、Nginx反向代理、Linux基础。当你对项目了如指掌时面试就变成了分享经验而不是被动答题。1.3 从“学习者”到“构建者”的思维转变课程作业通常是实现既定需求。而个人博客项目你需要自己扮演产品经理和架构师设计数据库表结构、规划API接口、构思用户体验。这种主动设计和决策的能力正是初级开发者向中级开发者迈进的关键。2. 技术选型与核心概念解读我们的技术栈非常明确Spring Boot 2.x Vue 3。这是目前企业级应用和现代前端开发最主流、最稳定的组合之一。2.1 后端技术栈详解Spring Boot我们的基石。它通过“约定大于配置”的理念极大地简化了Spring应用的初始搭建和开发过程。我们不需要再被繁琐的XML配置困扰。MyBatis-Plus一款强大的MyBatis增强工具。它提供了通用的Mapper和Service只需简单配置即可实现单表CRUD操作极大提升开发效率。同时它完全兼容原生MyBatis的功能复杂SQL依然可以手写。Spring Security JWT用于解决安全问题。Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。我们将结合JWTJSON Web Token实现无状态的API认证。用户登录后服务器生成一个Token返回给前端前端后续请求在Header中携带此Token服务器验证Token有效性即可。MySQL关系型数据库用于存储用户、文章、分类等结构化数据。Maven项目构建和依赖管理工具。2.2 前端技术栈详解Vue 3 Composition APIVue.js的最新主要版本。我们使用script setup语法和Composition API相比Vue 2的Options API逻辑组织更灵活代码复用性更强。Vite下一代前端构建工具。它基于原生ES模块启动速度极快热更新迅速开发体验远超传统的Webpack。Vue Router 4Vue.js的官方路由管理器用于构建单页面应用SPA。PiniaVue官方推荐的状态管理库替代了之前的Vuex。它的API更简洁且完美支持TypeScript。Element Plus基于Vue 3的桌面端组件库。我们将用它快速搭建出美观且交互一致的博客管理后台界面。Axios基于Promise的HTTP客户端用于向后端发起请求。2.3 前后端分离架构这是现代Web开发的标准模式。后端仅提供数据接口API前端负责页面渲染和用户交互。两者通过HTTP协议通常是JSON格式进行通信。这种架构的好处是前后端可以独立开发、测试和部署职责清晰。3. 环境准备与项目初始化在开始编码前请确保你的开发环境已就绪。3.1 基础环境准备JDK版本 8 或 11推荐11。安装后配置JAVA_HOME环境变量。Node.js版本 16 或以上。安装后会自带包管理工具npm。建议使用nvmNode Version Manager来管理多个Node版本。MySQL版本 5.7 或 8.0。安装并启动服务记住root密码。IDE后端IntelliJ IDEA社区版或旗舰版或 Eclipse需安装Spring插件。前端Visual Studio Code轻量高效或 WebStorm。MavenIDEA通常内置也可单独安装。浏览器Chrome 或 Edge用于调试前端。3.2 创建后端Spring Boot项目使用Spring Initializr start.spring.io 或IDEA内置的Spring Initializr创建项目。Project: MavenLanguage: JavaSpring Boot: 选择2.7.x或3.x本文以2.7.x为例3.x需注意部分依赖变化Project Metadata:Group:com.yournameArtifact:blog-backendDependencies: 添加以下依赖Spring WebMyBatis FrameworkMySQL DriverLombok简化POJO类代码生成项目后用IDEA打开。3.3 创建前端Vue 3项目打开终端命令行执行以下命令# 使用Vite官方模板创建项目 npm create vuelatest blog-frontend # 根据提示进行选择 # ✔ Add TypeScript? ... No (根据喜好选择本文用JavaScript) # ✔ Add JSX Support? ... No # ✔ Add Vue Router for Single Page Application development? ... Yes # ✔ Add Pinia for state management? ... Yes # ✔ Add Vitest for Unit Testing? ... No # ✔ Add an End-to-End Testing Solution? » No # ✔ Add ESLint for code quality? ... Yes # 进入项目目录并安装依赖 cd blog-frontend npm install # 安装额外需要的依赖库 npm install axios element-plus element-plus/icons-vue # 如果网络慢可以使用淘宝镜像npm config set registry https://registry.npmmirror.com至此前后端两个空项目骨架已经创建完成。4. 后端核心功能实现我们将后端功能模块化逐步实现。4.1 数据库设计与建表在MySQL中创建数据库blog_db并执行以下SQL创建核心表-- 用户表 CREATE TABLE sys_user ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键, username varchar(64) NOT NULL COMMENT 用户名, password varchar(128) NOT NULL COMMENT 密码加密后, avatar varchar(512) DEFAULT NULL COMMENT 头像链接, email varchar(128) DEFAULT NULL COMMENT 邮箱, status tinyint NOT NULL DEFAULT 1 COMMENT 状态0-禁用1-正常, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 文章表 CREATE TABLE blog_article ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键, user_id bigint NOT NULL COMMENT 作者ID, category_id bigint DEFAULT NULL COMMENT 分类ID, title varchar(256) NOT NULL COMMENT 文章标题, cover_image varchar(512) DEFAULT NULL COMMENT 封面图, content longtext NOT NULL COMMENT 文章内容HTML格式, summary varchar(512) DEFAULT NULL COMMENT 文章摘要, view_count int DEFAULT 0 COMMENT 阅读量, is_top tinyint DEFAULT 0 COMMENT 是否置顶0-否1-是, status tinyint NOT NULL DEFAULT 1 COMMENT 状态0-草稿1-发布, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_category_id (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT文章表; -- 分类表 CREATE TABLE blog_category ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键, name varchar(64) NOT NULL COMMENT 分类名称, description varchar(256) DEFAULT NULL COMMENT 分类描述, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT文章分类表;4.2 集成MyBatis-Plus与配置在pom.xml中添加MyBatis-Plus依赖dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3/version /dependency配置application.yml或application.propertiesserver: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/blog_db?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai username: root password: your_password # 替换为你的数据库密码 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印SQL生产环境关闭 global-config: db-config: logic-delete-field: deleted # 全局逻辑删除字段名如果表中有此字段 logic-delete-value: 1 # 逻辑已删除值 logic-not-delete-value: 0 # 逻辑未删除值4.3 实体类、Mapper与Service以User实体为例展示MyBatis-Plus的便捷用法。实体类 (User.java):// 文件路径src/main/java/com/yourname/blogbackend/entity/User.java package com.yourname.blogbackend.entity; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.time.LocalDateTime; Data TableName(sys_user) // 指定表名 public class User { TableId(type IdType.AUTO) // 主键自增 private Long id; private String username; private String password; private String avatar; private String email; private Integer status; TableField(fill FieldFill.INSERT) // 插入时自动填充 private LocalDateTime createTime; TableField(fill FieldFill.INSERT_UPDATE) // 插入和更新时自动填充 private LocalDateTime updateTime; }Mapper接口 (UserMapper.java):// 文件路径src/main/java/com/yourname/blogbackend/mapper/UserMapper.java package com.yourname.blogbackend.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.yourname.blogbackend.entity.User; import org.apache.ibatis.annotations.Mapper; Mapper public interface UserMapper extends BaseMapperUser { // 继承BaseMapper后基础的CRUD方法已自动拥有无需编写XML }Service接口与实现 (IUserService.javaUserServiceImpl.java):// 文件路径src/main/java/com/yourname/blogbackend/service/IUserService.java package com.yourname.blogbackend.service; import com.baomidou.mybatisplus.extension.service.IService; import com.yourname.blogbackend.entity.User; public interface IUserService extends IServiceUser { // 可以在此定义复杂的业务方法 User getUserByUsername(String username); }// 文件路径src/main/java/com/yourname/blogbackend/service/impl/UserServiceImpl.java package com.yourname.blogbackend.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yourname.blogbackend.entity.User; import com.yourname.blogbackend.mapper.UserMapper; import com.yourname.blogbackend.service.IUserService; import org.springframework.stereotype.Service; Service public class UserServiceImpl extends ServiceImplUserMapper, User implements IUserService { Override public User getUserByUsername(String username) { // 使用MyBatis-Plus的QueryWrapper构建查询条件 QueryWrapperUser queryWrapper new QueryWrapper(); queryWrapper.eq(username, username); return this.getOne(queryWrapper); } }4.4 实现JWT认证与授权添加JWT依赖如jjwt到pom.xml。创建JwtUtil工具类用于生成和解析Token。创建LoginController处理登录请求验证用户名密码后生成JWT返回。创建JwtAuthenticationTokenFilter过滤器在请求到达Controller前从Header中解析Token并设置用户身份信息到SecurityContext。配置Spring Security放行登录接口和静态资源保护其他API。4.5 实现文章管理API创建ArticleController提供标准的RESTful接口GET /api/articles分页获取文章列表。GET /api/articles/{id}根据ID获取文章详情。POST /api/articles创建新文章需要登录。PUT /api/articles/{id}更新文章需要登录且为作者本人。DELETE /api/articles/{id}删除文章需要登录且为作者本人或管理员。关键点分页查询使用MyBatis-Plus的Page对象创建和更新时需要从SecurityContext中获取当前登录用户的ID作为user_id。5. 前端核心功能实现前端我们将构建一个包含后台管理界面和博客展示界面的SPA。5.1 项目结构初始化与配置在src目录下创建清晰的模块化结构src/ ├── api/ # 所有axios请求封装 ├── assets/ # 静态资源 ├── components/# 公共组件 ├── router/ # 路由配置 ├── stores/ # Pinia状态管理 ├── utils/ # 工具函数 └── views/ # 页面组件配置main.js引入Element Plus和图标库// 文件路径src/main.js import { createApp } from vue import App from ./App.vue import router from ./router import { createPinia } from pinia // 引入Element Plus import ElementPlus from element-plus import element-plus/dist/index.css import * as ElementPlusIconsVue from element-plus/icons-vue const app createApp(App) // 注册所有图标 for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } app.use(createPinia()) app.use(router) app.use(ElementPlus) app.mount(#app)5.2 封装Axios与API模块在src/utils/request.js中创建axios实例并配置请求/响应拦截器用于自动添加JWT Token、处理通用错误等。// 文件路径src/utils/request.js import axios from axios import { ElMessage } from element-plus import router from /router const service axios.create({ baseURL: http://localhost:8080/api, // 后端API基础地址 timeout: 10000 }) // 请求拦截器 service.interceptors.request.use( config { // 从localStorage中获取token const token localStorage.getItem(blog_token) if (token) { config.headers[Authorization] Bearer ${token} } return config }, error { return Promise.reject(error) } ) // 响应拦截器 service.interceptors.response.use( response { const res response.data // 假设后端统一返回格式为 { code: 200, data: ..., message: success } if (res.code ! 200) { ElMessage.error(res.message || Error) // 如果是401未授权跳转到登录页 if (res.code 401) { localStorage.removeItem(blog_token) router.push(/login) } return Promise.reject(new Error(res.message || Error)) } else { return res.data // 直接返回data字段 } }, error { ElMessage.error(error.message || Request Error) return Promise.reject(error) } ) export default service在src/api目录下创建模块化的API文件如article.js// 文件路径src/api/article.js import request from /utils/request export function getArticleList(params) { return request({ url: /articles, method: get, params // params 包含 pageNum, pageSize, categoryId 等 }) } export function createArticle(data) { return request({ url: /articles, method: post, data }) } // ... 其他增删改查接口5.3 实现登录与权限管理创建登录页面 (Login.vue)调用登录API成功后保存Token至localStorage并跳转到后台首页。在路由守卫 (router/index.js的beforeEach钩子) 中检查访问权限保护需要登录的路由。使用Pinia (src/stores/user.js) 全局管理用户登录状态和基本信息。5.4 构建后台管理界面使用Element Plus的布局组件 (el-container,el-header,el-aside,el-main) 搭建经典的后台管理布局。侧边栏菜单使用el-menu组件根据路由动态生成。文章管理页表格展示使用el-table展示文章列表包含标题、分类、状态、发布时间等列。分页组件使用el-pagination并与后端分页API联动。操作按钮编辑、删除、发布/下架。搜索与筛选表单区域可按标题、分类、状态筛选。文章编辑页集成富文本编辑器推荐使用wangeditor/editor-for-vue(轻量) 或tinymce-vue(功能强大)。表单验证使用el-form配合async-validator。5.5 构建博客前台展示页创建一个新的路由和视图用于对外展示博客。首页 (HomeView.vue)展示文章列表、分类导航、热门文章等。文章详情页 (ArticleDetail.vue)根据路由参数id请求文章详情并渲染。布局可以设计得更简洁美观与后台管理风格区分开。6. 前后端联调与运行6.1 解决跨域问题前端运行在localhost:5173Vite默认端口后端在localhost:8080浏览器会因同源策略阻止请求。在后端添加一个全局CORS配置即可解决// 文件路径src/main/java/com/yourname/blogbackend/config/CorsConfig.java package com.yourname.blogbackend.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.WebMvcConfigurer; Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) // 对所有接口 .allowedOriginPatterns(*) // 允许所有来源生产环境应指定具体域名 .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }6.2 启动与验证启动后端在IDEA中运行BlogBackendApplication的main方法控制台看到Spring Boot启动日志无报错。启动前端在blog-frontend目录下执行npm run dev。终端会输出本地访问地址通常是http://localhost:5173。功能验证打开前端页面尝试登录需先在数据库sys_user表手动插入一条用户记录密码需为BCrypt加密后的字符串。登录成功后进入后台尝试创建分类、撰写并发布文章。访问博客前台首页查看发布的文章是否正常显示。7. 常见问题与排查思路在开发过程中你几乎一定会遇到下面这些问题。问题现象可能原因排查方式解决方案前端页面空白控制台报错Failed to load resource: net::ERR_CONNECTION_REFUSED后端服务未启动或端口不对。1. 检查后端控制台是否成功启动。2. 在浏览器直接访问http://localhost:8080/api/test(假设有个测试接口)。确保后端服务在指定端口正常运行。前端请求接口返回4041. 后端接口路径写错。2. 后端Controller未添加RestController或RequestMapping。1. 核对前端请求URL和后端接口定义。2. 在后端IDE中使用“Find in Path”搜索接口路径。修正URL或后端注解。使用Swagger或Knife4j生成API文档有助于核对。登录成功但后续请求返回401 (Unauthorized)1. 前端未正确携带Token。2. Token已过期。3. 后端JWT解析失败。1. 浏览器开发者工具Network面板查看请求Header中是否有Authorization。2. 检查后端JWT密钥、过期时间配置。3. 查看后端日志是否有解析异常。1. 检查axios拦截器逻辑。2. 延长JWT过期时间或实现Token刷新机制。3. 确保前后端使用的JWT密钥一致。插入中文到MySQL出现乱码数据库、表或连接字符串字符集不是utf8mb4。1. 检查数据库CREATE DATABASE语句。2. 检查表CREATE TABLE语句。3. 检查JDBC连接URL参数。确保数据库、表字符集为utf8mb4连接URL添加characterEncodingutf-8。MyBatis-Plus查询不到数据1. 实体类字段名与数据库列名未正确映射。2. 逻辑删除字段导致查询自动附加条件。1. 使用TableField注解指定映射。2. 检查实体类是否有deleted字段并查看全局配置。1. 开启SQL日志 (mybatis-plus.configuration.log-impl)查看实际执行的SQL。2. 在查询时使用.eq(“deleted”, 0)覆盖全局逻辑删除条件。Element Plus组件样式未生效1. 未正确引入CSS文件。2. Vue版本与Element Plus版本不兼容。1. 检查main.js中是否import element-plus/dist/index.css。2. 查看控制台是否有相关警告。1. 确保CSS引入语句在createApp之后app.mount()之前。2. 查阅Element Plus官网的安装指南核对版本。8. 项目优化与进阶建议完成基础功能后你可以从以下几个方向深化项目这会让你的项目在简历中更加出彩。8.1 安全性加固密码加密务必使用BCryptPasswordEncoder等强哈希算法存储密码切勿明文存储。SQL注入防护坚持使用MyBatis-Plus的QueryWrapper或MyBatis的#{}预编译杜绝字符串拼接SQL。XSS防护对富文本内容入库前进行白名单过滤如使用Jsoup库输出时进行转义。接口限流与防刷对登录、评论等接口添加限流如使用Guava RateLimiter或Spring Boot Actuator。敏感信息脱敏在日志或返回前端的用户信息中对手机号、邮箱等进行脱敏处理。8.2 性能与体验优化后端缓存对不常变动的数据如分类列表、热门文章使用Redis或Caffeine进行缓存。数据库索引优化为article表的user_id,category_id,create_time等查询条件字段添加索引。前端路由懒加载在Vue Router配置中使用() import(‘…’)语法拆分代码包提升首屏加载速度。图片上传与CDN将用户上传的图片存储到OSS对象存储服务如阿里云OSS、七牛云并返回CDN链接减轻服务器压力。文章列表分页优化避免使用limit offset, size进行深度分页可改用基于id的条件查询。8.3 功能扩展文章标签系统实现多对多关系让一篇文章拥有多个标签。文章评论与回复设计评论表支持楼中楼回复。全文搜索集成Elasticsearch为文章标题和内容提供高效的全文检索功能。数据统计仪表盘在后台首页展示文章数、用户数、访问趋势等图表可集成ECharts。SEO优化为博客前台页面生成静态化或服务端渲染SSR添加合理的meta标签利于搜索引擎收录。8.4 部署上线后端打包使用mvn clean package生成可执行的JAR文件。前端构建运行npm run build生成静态文件到dist目录。服务器准备购买一台云服务器如阿里云ECS安装JDK、MySQL、Nginx。部署流程将后端JAR文件上传至服务器使用nohup java -jar blog-backend.jar 后台运行。将前端dist目录下的文件上传至服务器配置Nginx将请求代理到后端API并托管前端静态资源。为域名配置SSL证书启用HTTPS。完成以上步骤你的个人博客就不再是本地玩具而是一个真正的线上项目。在简历中附上项目地址其说服力将成倍增加。这个博客项目是一个起点而非终点。它的价值在于为你提供了一个完整的、可扩展的现代Web应用实践范本。通过它你不仅学会了Spring Boot和Vue 3的语法更理解了如何将多个技术组件有机地组合在一起去解决一个真实的业务问题。接下来你可以根据“优化与进阶建议”部分选择一两个方向深入实践。例如尝试集成Redis缓存或者为博客添加一个简单的评论功能。每解决一个实际问题你对整个技术体系的理解就会加深一层。当你再去学习微服务、分布式、高并发等更高级的主题时你会发现自己有了更扎实的根基和更明确的目标。