拓冰建站拓冰建站
首页 / 资讯中心 / 正文

SpringBoot + Vue3 前后端分离图书管理系统实战

每年毕业季全世界都会诞生成千上万套“图书管理系统”它几乎是计算机专业学生绕不开的一个名字。但也正因为项目太多反而暴露了一个尴尬现实网上搜到的 SpringBoot Vue 图书管理系统源码要么还停留在 Vue2 时代要么缺数据库脚本、缺依赖配置、接口和页面根本对不上初学者下载下来根本跑不起来。在我看来图书管理系统被严重低估了。它的业务模型足够简单技术链路却足够完整——从 MySQL 建表、MyBatis-Plus 数据访问、SpringBoot 接口开发到 Vue3 页面渲染、axios 请求、前后端联调几乎覆盖了一个后台管理系统的全部核心环节。你不需要理解复杂的订单状态机也不需要处理高并发缓存就可以在一个晚上跑通整条前后端分离开发链路。这篇文章不做花哨的扩展功能目标只有一个带你搭出一个可运行的 SpringBoot Vue3 前后端分离图书管理系统支持完整的增删改查、关键字查询并解释每一步为什么这么做。你还会看到我在实际开发中最常遇到的几个坑以及毕设答辩和写简历时可以借鉴的描述方式。1. 这个项目真正要解决的问题1.1 一个被低估的入门项目很多人在学完 Java 基础和 SpringBoot 基础后会陷入一种“知识都听过但组合不起来”的状态。单体接口练习只涉及一个 Controller 加一个 Service纯前端页面又只把数据写死在 JS 里。真正需要同时操作数据库、后端接口、前端页面的项目才是从“写代码”走向“做项目”的转折点。图书管理系统恰好是这个转折点的最佳载体。它本质上是对一张book表的增删改查但围绕这张表你需要搭建完整工程、配置数据源、设计 RESTful 接口、处理跨域、封装前端请求、渲染表格和弹窗。这一整套流程在任何后台管理系统中都会重复出现。换句话说这个项目真正解决的不是“图书怎么管理”而是“一个完整的前后端分离项目是如何从零组织起来的”。1.2 你能从中学到什么跑通这个项目之后你会掌握以下能力使用 SpringBoot 编写 RESTful API而不是写一堆页面跳转使用 MyBatis-Plus 完成单表 CRUD减少重复 SQL理解 MySQL 表结构设计的基本规范使用 Vue3 Vite 创建工程化前端项目使用 Element Plus 快速搭建后台管理页面解决前后端联调中的跨域、端口、字段映射问题如果你明年要毕业或者正在准备找 Java 后端方向的工作这个项目是一个短期内可完成的简历项目。它不算“高大上”但胜在结构完整、逻辑清晰面试官提问时你容易讲透彻。1.3 项目功能清单本文实现的图书管理系统包含以下功能功能模块说明图书列表表格展示图书信息支持按书名或作者模糊搜索新增图书填写书名、作者、ISBN、出版社、价格、库存、分类编辑图书回显已有数据修改后保存删除图书按主键删除指定记录数据校验前端表单必填校验后端接口统一返回结构2. 核心概念前后端分离到底在分离什么2.1 一次请求的生命周期在动手写代码前先理解一次完整请求的流转过程。用户在浏览器输入书名并点击查询前端的 Vue 组件捕获到搜索事件通过 axios 发起 HTTP GET 请求/api/book/list?keyword三体。后端 SpringBoot 的 Controller 接住请求调用 Service 层Service 再通过 MyBatis-Plus 查询 MySQL 数据库把结果包装成 JSON 返回给前端。前端拿到 JSON 后更新表格数据并渲染到页面上。整个过程中前后端唯一约定好的东西就是接口文档请求地址是什么、请求方式是 GET 还是 POST、参数叫什么、返回结构长什么样。前端不关心数据库里的表结构后端也不关心页面的按钮样式。2.2 技术选型与各层职责本文选用的技术组合是 SpringBoot MyBatis-Plus MySQL Vue3 Vite Element Plus这也是目前国内中小型后台管理系统常见的组合。端技术职责后端Spring Boot提供 HTTP 接口处理业务逻辑持久层MyBatis-Plus简化 SQL自动生成单表 CRUD数据库MySQL持久化存储图书数据前端Vue3 Vite页面渲染与用户交互UI 库Element Plus表格、表单、弹窗、按钮组件网络请求axios前端调用后端接口2.3 为什么不再用 JSP过去做 Java Web 项目常用 JSP 将 Java 代码和 HTML 混在同一个文件里。改一次页面样式可能要重新编译整个应用前端和后端开发者也会互相阻塞。前后端分离之后后端只需要返回 JSON 数据前端独立开发和部署。某个页面升级不必重启后端服务后端某个接口优化也不必重新发布前端。这一点在团队协作中价值巨大。对于学生项目分离开发还有一个额外好处你可以把后端部署在云服务器 8080 端口前端部署在 Nginx更接近真实企业部署方式。3. 环境准备与前置条件3.1 工具清单在开始之前请确认本机已经安装以下软件JDK 8 或 17本文示例按 JDK 8 语法编写17 同样兼容Maven 3.6 及以上Node.js 16 及以上MySQL 5.7 或 8.0IDEA 或 Eclipse后端开发VS Code 或其他前端编辑器前端开发Maven 和 Node.js 的安装都比较简单安装完成后需要配置环境变量。Windows 环境下重点检查MAVEN_HOME、JAVA_HOME和 PATH 配置是否正确。3.2 环境验证命令打开命令行终端依次执行以下命令如果没有报错说明环境基本可用。java -version mvn -version node -v npm -v mysql --version我遇到过不少读者的启动失败案例最终原因不是代码问题而是 mysql 命令都还提示“不是内部或外部命令”。所以我的建议是在写任何业务代码之前先把这几条命令跑通。3.3 版本组合建议SpringBoot 版本选择上我建议普通毕设项目使用 2.7.x它的资料最多、兼容性最广MyBatis-Plus 的集成也最省心。如果你选择 SpringBoot 3.x需要注意 MyBatis-Plus 需要引入mybatis-plus-spring-boot3-starter部分旧教程里的mybatis-plus-boot-starter会启动报错。前端使用 Vue3 Vite。Vite 冷启动非常快开发体验比 Vue CLI 好很多现在官方也推荐 Vite 作为 Vue 项目的默认构建工具。4. 后端SpringBoot 接口开发4.1 创建工程与目录结构后端工程可以有两种方式创建一是访问 Spring Initializr 网站生成压缩包二是在 IDEA 中直接选择 Spring Initializr 创建。创建时选择 Java 8、Spring Web 依赖即可其他依赖我们手动添加方便说清楚每个依赖的作用。创建完成后在com.example.library包下建立以下包结构src/main/java/com/example/library ├── common // 通用返回结果 ├── config // 配置类 ├── controller // 控制器 ├── entity // 实体类 ├── mapper // MyBatis-Plus Mapper ├── service // 服务接口 └── service/impl // 服务实现4.2 pom.xml 依赖配置打开pom.xml在 dependencies 节点添加以下依赖。dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies这里的核心是mybatis-plus-boot-starter。MyBatis-Plus 不是替代 MyBatis而是在 MyBatis 之上封装了单表通用的 CRUD 方法省去我们手写大量重复 SQL。mysql-connector-j是 MySQL 驱动由 SpringBoot 父工程统一管理版本不需要手动写版本号。4.3 application.yml 数据源配置在src/main/resources目录下创建application.yml配置服务端口、数据库连接和 MyBatis-Plus 日志。server: port: 8080 spring: application: name: library-backend datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/library?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: auto注意数据库密码请改成你自己的 MySQL 密码。serverTimezoneAsia/Shanghai主要是避免 MySQL 8.x 的时区报错characterEncodingutf8是为了防止中文乱码。4.4 数据库设计 SQL 脚本本项目的核心表是图书表字段设计尽量贴近真实图书管理需求但不过度设计。CREATE DATABASE IF NOT EXISTS library DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE library; DROP TABLE IF EXISTS book; CREATE TABLE book ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, name VARCHAR(100) NOT NULL COMMENT 书名, author VARCHAR(100) DEFAULT NULL COMMENT 作者, isbn VARCHAR(32) DEFAULT NULL COMMENT ISBN, publisher VARCHAR(100) DEFAULT NULL COMMENT 出版社, price DECIMAL(10,2) DEFAULT 0.00 COMMENT 价格, stock INT DEFAULT 0 COMMENT 库存, category VARCHAR(50) DEFAULT NULL COMMENT 分类, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT图书表;在实际项目中每张表最好都包含create_time和update_time两个字段。它们不参与业务逻辑但在排查数据问题时非常有用。本文通过数据库默认值自动维护不需要在 Java 代码里手动赋值。可以在建表后插入几条测试数据方便前端联调时立即看到效果。INSERT INTO book (name, author, isbn, publisher, price, stock, category) VALUES (三体, 刘慈欣, 9787536692930, 重庆出版社, 30.00, 10, 科幻), (活着, 余华, 9787506365437, 作家出版社, 28.00, 15, 小说), (深入理解Java虚拟机, 周志明, 9787111641245, 机械工业出版社, 99.00, 8, 技术);4.5 实体类与 Mapper实体类对应数据库表结构。使用 Lombok 的Data注解简化 getter/setter。// 文件路径src/main/java/com/example/library/entity/Book.java package com.example.library.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; Data TableName(book) public class Book { TableId(type IdType.AUTO) private Long id; private String name; private String author; private String isbn; private String publisher; private BigDecimal price; private Integer stock; private String category; private LocalDateTime createTime; private LocalDateTime updateTime; }Mapper 接口只需要继承 MyBatis-Plus 的BaseMapper就会自动拥有selectById、insert、updateById、deleteById等方法。// 文件路径src/main/java/com/example/library/mapper/BookMapper.java package com.example.library.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.library.entity.Book; import org.apache.ibatis.annotations.Mapper; Mapper public interface BookMapper extends BaseMapperBook { }4.6 Service 层Service 层的价值在于隔离 Controller 和 Mapper。简单项目可能觉得多此一举但一旦业务复杂起来比如增加借还书逻辑、库存校验你就会感谢这层抽象带来的清晰度。// 文件路径src/main/java/com/example/library/service/BookService.java package com.example.library.service; import com.baomidou.mybatisplus.extension.service.IService; import com.example.library.entity.Book; public interface BookService extends IServiceBook { }// 文件路径src/main/java/com/example/library/service/impl/BookServiceImpl.java package com.example.library.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.library.entity.Book; import com.example.library.mapper.BookMapper; import com.example.library.service.BookService; import org.springframework.stereotype.Service; Service public class BookServiceImpl extends ServiceImplBookMapper, Book implements BookService { }MyBatis-Plus 的ServiceImpl已经实现了IService中的常用方法我们目前不需要额外写业务逻辑。但这层不要在 Controller 中省略因为后续加业务校验时改动会集中在 Service 里。4.7 统一返回结构与 Controller为了让前端更容易处理接口响应我习惯定义一个统一的ResultT返回结构。// 文件路径src/main/java/com/example/library/common/Result.java package com.example.library.common; import lombok.Data; Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(success); result.setData(data); return result; } public static T ResultT error(String message) { ResultT result new Result(); result.setCode(500); result.setMessage(message); return result; } }Controller 层是前端请求的入口本文提供三个接口查询列表、保存、删除。// 文件路径src/main/java/com/example/library/controller/BookController.java package com.example.library.controller; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.example.library.common.Result; import com.example.library.entity.Book; import com.example.library.service.BookService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; RestController RequestMapping(/api/book) public class BookController { Autowired private BookService bookService; GetMapping(/list) public ResultListBook list(RequestParam(required false) String keyword) { LambdaQueryWrapperBook wrapper new LambdaQueryWrapper(); if (keyword ! null !keyword.trim().isEmpty()) { wrapper.like(Book::getName, keyword).or().like(Book::getAuthor, keyword); } wrapper.orderByDesc(Book::getId); return Result.success(bookService.list(wrapper)); } PostMapping(/save) public ResultVoid save(RequestBody Book book) { boolean success bookService.saveOrUpdate(book); return success ? Result.success(null) : Result.error(保存失败); } DeleteMapping(/delete/{id}) public ResultVoid delete(PathVariable Long id) { boolean success bookService.removeById(id); return success ? Result.success(null) : Result.error(删除失败); } }这里有几个细节值得说明。save接口既处理新增也处理编辑当book.id为空时执行插入当book.id不为空时执行更新这是 MyBatis-Plus 的saveOrUpdate方法帮我们完成的。模糊查询使用LambdaQueryWrapper的like方法MyBatis-Plus 会自动为keyword加上%通配符比手写字符串拼接 SQL 更安全。RestController意味着所有方法返回值都会自动转换为 JSON不需要额外配置 JSON 转换器。4.8 跨域配置如果前端使用了 Vite 代理后端可以不配置跨域。但很多读者习惯直接用http://localhost:5173请求http://localhost:8080这种情况下必须配置跨域否则浏览器会拦截响应。这里预留一个后端跨域配置类两种方案选一种即可。// 文件路径src/main/java/com/example/library/config/CorsConfig.java package com.example.library.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.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); } }4.9 启动后端后端启动比较简单。在 IDEA 中运行启动类LibraryApplication控制台出现Tomcat started on port(s): 8080说明启动成功。此时可以先在浏览器访问http://localhost:8080/api/book/list能看到 JSON 数据就说明后端和数据库已经打通。5. 前端Vue3 页面开发5.1 创建 Vite 工程在命令行执行下面的命令创建一个 Vue3 工程。book-front是项目目录名你可以自己修改。npm create vitelatest book-front -- --template vue cd book-front npm install注意如果你使用的 npm 版本比较老执行npm create vite时可能出现交互式询问按照提示选择Vue和JavaScript即可。这个过程会自动生成index.html、src/main.js、src/App.vue等基础文件。5.2 安装依赖与代理配置安装 Element Plus 和 axios。npm install element-plus axios在src/main.js中引入 Element Plus。// 文件路径src/main.js import { createApp } from vue import ElementPlus from element-plus import element-plus/dist/index.css import App from ./App.vue const app createApp(App) app.use(ElementPlus) app.mount(#app)然后修改vite.config.js配置开发环境代理。代理的作用是解决跨域浏览器请求/api开头的地址时Vite 开发服务器把请求转发到http://localhost:8080浏览器始终只和前端同源不会触发 CORS 限制。// 文件路径vite.config.js import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })5.3 封装 axios 请求在src/api目录下创建book.js统一封装图书接口。// 文件路径src/api/book.js import axios from axios const request axios.create({ baseURL: /api, timeout: 5000 }) export function listBooks(keyword) { return request.get(/book/list, { params: { keyword } }) } export function saveBook(book) { return request.post(/book/save, book) } export function deleteBook(id) { return request.delete(/book/delete/${id}) }封装 request 的好处是以后如果所有接口都需要携带 token可以直接在拦截器里统一处理而不必每个接口都写一遍。5.4 图书管理页面组件在src/views目录下创建BookManage.vue。这是整个前端最核心的组件包含搜索栏、图书表格、新增编辑弹窗。!-- 文件路径src/views/BookManage.vue -- template div classbook-manage h2图书管理/h2 div classtoolbar el-input v-modelkeyword placeholder请输入书名或作者 clearable stylewidth: 220px; margin-right: 12px changeloadData / el-button typeprimary clickopenDialog()新增图书/el-button /div el-table :datatableData border stripe v-loadingloading el-table-column propid labelID width70 / el-table-column propname label书名 min-width140 / el-table-column propauthor label作者 width110 / el-table-column propisbn labelISBN width140 / el-table-column proppublisher label出版社 width150 / el-table-column propprice label价格 width90 / el-table-column propstock label库存 width80 / el-table-column propcategory label分类 width90 / el-table-column propcreateTime label创建时间 width180 / el-table-column label操作 width150 fixedright template #default{ row } el-button link typeprimary clickopenDialog(row)编辑/el-button el-button link typedanger clickhandleDelete(row.id)删除/el-button /template /el-table-column /el-table el-dialog v-modeldialogVisible :titleform.id ? 编辑图书 : 新增图书 width520px el-form :modelform label-width80px el-form-item label书名 el-input v-modelform.name / /el-form-item el-form-item label作者 el-input v-modelform.author / /el-form-item el-form-item labelISBN el-input v-modelform.isbn / /el-form-item el-form-item label出版社 el-input v-modelform.publisher / /el-form-item el-form-item label价格 el-input-number v-modelform.price :precision2 :min0 / /el-form-item el-form-item label库存 el-input-number v-modelform.stock :min0 / /el-form-item el-form-item label分类 el-input v-modelform.category / /el-form-item /el-form template #footer el-button clickdialogVisible false取消/el-button el-button typeprimary clickhandleSave保存/el-button /template /el-dialog /div /template script setup import { ref, onMounted } from vue import { ElMessage, ElMessageBox } from element-plus import { listBooks, saveBook, deleteBook } from ../api/book const loading ref(false) const keyword ref() const tableData ref([]) const dialogVisible ref(false) const emptyForm { id: null, name: , author: , isbn: , publisher: , price: 0, stock: 0, category: } const form ref({ ...emptyForm }) async function loadData() { loading.value true try { const res await listBooks(keyword.value) tableData.value res.data.data || [] } finally { loading.value false } } function openDialog(row) { if (row) { form.value { ...row } } else { form.value { ...emptyForm } } dialogVisible.value true } async function handleSave() { if (!form.value.name) { ElMessage.warning(请填写书名) return } const res await saveBook(form.value) if (res.data.code 200) { ElMessage.success(form.value.id ? 更新成功 : 新增成功) dialogVisible.value false loadData() } else { ElMessage.error(res.data.message || 保存失败) } } async function handleDelete(id) { await ElMessageBox.confirm(确定删除这条图书记录吗, 提示, { type: warning }) const res await deleteBook(id) if (res.data.code 200) { ElMessage.success(删除成功) loadData() } } onMounted(() { loadData() }) /script style scoped .book-manage { padding: 20px; } .toolbar { margin-bottom: 16px; } /style这段代码里loadData负责加载表格数据openDialog负责区分新增和编辑handleSave负责提交表单handleDelete带二次确认后删除。组件结构清晰你可以在此基础上继续加分页、表单校验、导出等功能。最后修改src/App.vue引入 BookManage 组件。!-- 文件路径src/App.vue -- template BookManage / /template script setup import BookManage from ./views/BookManage.vue /script这里没有引入 Vue Router因为项目只有一个页面。如果后续要加登录页、统计页再引入vue-router也不迟。5.5 启动前端在book-front目录执行npm run dev控制台输出Local: http://localhost:5173/后用浏览器访问http://localhost:5173就可以看到图书管理页面。6. 前后端联调与功能验证6.1 联调前检查清单前后端分别启动后先不要急着点按钮按下面清单检查一遍。后端服务是否运行在 8080 端口MySQL 是否启动library库是否存在前端控制台是否有报错浏览器开发者工具的 Network 面板中请求/api/book/list是否返回 200如果请求失败查看前端代理配置和后端跨域配置6.2 按流程验证增删改查建议按照下面的顺序做全流程验证。打开http://localhost:5173页面应该显示三条测试数据在搜索框输入“三体”点击搜索表格只显示《三体》清空搜索框点击“新增图书”填写信息并保存新记录出现在表格顶部点击某行“编辑”修改书名或价格保存后表格自动刷新点击某行“删除”确认后该记录从表格消失打开浏览器 F12 开发者工具切换 Network 标签刷新页面可以看到后端接口返回的 JSON 数据结构如果每一步都通过说明一个独立的增删改查闭环已经打通。这个闭环是后续所有功能扩展的基础。7. 常见问题与排查方法7.1 典型问题表格问题现象可能原因排查方式解决方案后端启动报数据库连接失败MySQL 未启动、密码错误、库不存在查看控制台日志中的根因异常启动 MySQL检查 application.yml 配置前端访问接口 404代理没生效或后端接口路径不一致看 Network 请求实际 URL检查 vite.config.js 代理和 Controller 的 RequestMapping前端访问接口 500后端抛异常、SQL 错误查看后端控制台堆栈根据日志定位到具体代码行返回中文乱码数据库连接 URL 未指定 utf8检查 application.yml 的连接串加上characterEncodingutf8接口返回字段是 null实体类属性名与表字段不一致对比数据库字段和实体类使用驼峰映射MyBatis-Plus 默认开启使用 SpringBoot 3.x 启动失败MyBatis-Plus 依赖用错看依赖冲突提示改为mybatis-plus-spring-boot3-starter7.2 三个最容易被忽视的坑第一个坑是数据库连接 URL 时区错误。MySQL 8.x 常常要求指定serverTimezone否则启动直接报“Cannot create PoolableConnectionFactory”你以为代码写错了其实只是连接串少了一个参数。第二个坑是前端请求地址写死成http://localhost:8080/api/...。如果后端配置了跨域这样做能通但如果后端没配置或者将来部署到服务器跨域策略改变会非常痛苦。更推荐用 Vite 代理让前端代码里始终写/api这样的相对路径。第三个坑是 Vue3 中不小心直接用this。Vue3 的组合式 API 里setup中并没有this不少从Vue2迁移过来的读者会在这里卡很久。本文示例全部使用ref和普通函数就是为了避免这个理解负担。7.3 如何快速定位问题我的排查顺序一般是前端 Network 面板是第一现场先确认请求有没有发出去、返回什么状态码如果请求没发出去问题在前端如果请求发出去了但状态码不是 200问题在后端如果状态码 200 但数据不对问题在接口逻辑或数据库。按这个顺序排查大多数问题几分钟内就能定位。8. 最佳实践、毕设答辩与简历写法8.1 工程结构建议即使是一个学习项目也要保持清晰的分层结构。entity只放数据实体mapper只做数据访问service只做业务逻辑controller只做参数接收和结果返回。不建议把 SQL 写在 Controller 中也不建议业务逻辑散落在多个 Controller 方法里。前端方面api目录集中管理接口非常值得养成习惯。页面组件内不直接写axios.get而是调用封装好的函数。这样接口地址变化时只需要改一个文件。8.2 代码层面建议第一参数校验不要只靠前端。例子中前端只做了“请填写书名”的非空校验后端也应该在保存接口中对必要字段进行校验避免绕过前端直接调用接口。第二删除操作建议做软删除而不是物理删除。所谓软删除是给表加一个deleted字段删除时执行UPDATE将字段置为 1查询时自动过滤。这样误删数据还能恢复面试时主动提出这一点会明显加分。第三日志记录要规范。后端接口的关键操作比如新增、删除建议打印操作日志。哪怕只是简单地用日志输出参数排查问题时也比“黑盒”接口好得多。8.3 简历上如何描述写简历时不要只写“实现了图书管理系统的增删改查”。更合适的写法是把技术点和难点描述出来基于 SpringBoot MyBatis-Plus 构建后端 RESTful API完成图书信息的增删改查与条件查询使用 Vue3 Vite Element Plus 搭建前端管理界面通过 axios 与后端接口联调通过 Vite 代理解决开发环境跨域问题前端组件化拆分实现新增编辑弹窗复用数据库表结构包含主键、常用字段、创建时间与更新时间符合基本设计规范面试官通常不会期待一个应届生做出多复杂的系统他们更看重你是否讲得清楚“为什么这么设计”。比如问“为什么用 MyBatis-Plus 而不用 JPA”你如果能回答“MyBatis-Plus 在国内团队普及率高学习成本低可以灵活控制 SQL而且和 MyBatis 无缝兼容”这就是一个不错的回答。8.4 答辩可能被问到的几个问题前后端分离和传统 MVC 有什么区别后端接口如何保证安全性如果数据量变大列表查询怎么优化为什么需要统一的返回结构Vite 代理和 Nginx 代理有什么区别这些问题不要求答得完美但至少不能完全没有思路。建议你把本文涉及的所有技术名词都查一遍做到“我用了它并且知道为什么用它”。9. 总结与下一步学习方向跑通这个项目意味着你已经具备了一个基础的全栈开发视角前端负责交互后端负责业务数据库负责持久化中间通过 HTTP 协议和 JSON 数据完成协作。这个视角的价值会伴随你进入任何一家公司、参与任何一个后台管理系统开发。下一步值得做的方向有三个。第一给项目加上分页和登录认证这是真实系统里绕不开的两个能力。第二把图书表扩展成“图书分类表 图书表 借阅记录表”通过三张表练习多表关联查询。第三尝试把前后端部署到云服务器后端打包成 jar 包运行前端构建后交给 Nginx 托管这一步会让你的简历上多一句“具备独立部署上线能力”。学习建议只有一条不要停留在看文章。打开你本地的 IDEA 和 VS Code手动把项目敲一遍。看代码和敲代码是两种完全不同的体验过程中遇到的所有报错都会成为你下次面试时能够从容回答的底气。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门