作为一名后端开发者,近年来我参与了多个基于 Spring Boot+Vue+MySQL 的全栈项目开发。从最初的技术选型踩坑,到后期的性能优化与业务落地,积累了不少实战经验。本文将从架构设计、核心技术实现、性能优化三个维度,分享这套技术栈的开发技巧与心得,附带可落地的代码实例,希望能给同路人带来帮助。
一、架构设计:先搭骨架,再填血肉
在全栈开发中,架构设计是基础。如果一开始就埋头写代码,后期很容易出现耦合度高、维护困难的问题。针对 Spring Boot+Vue+MySQL 的技术组合,我通常采用分层架构 + 前后端分离的设计思路。
1. 后端架构:分层解耦,职责单一
后端基于 Spring Boot 实现,遵循经典的分层架构,各层职责明确:
- 实体层(Entity):对应 MySQL 数据库表结构,使用 JPA/Hibernate 实现 ORM 映射。
- 数据访问层(Mapper/Repository):负责与数据库交互,可使用 MyBatis-Plus(简化 CRUD)或 Spring Data JPA。
- 业务逻辑层(Service):处理核心业务逻辑,包含事务控制、业务规则校验等。
- 控制层(Controller):接收前端请求,返回响应数据,处理跨域、参数校验等。
- 工具层(Util):封装通用工具类,如日期处理、加密解密、结果返回封装等。
2. 前端架构:组件化 + 路由分层
前端基于 Vue3(搭配 Vite)实现,采用组件化开发 + 路由分层的思路:
- 页面组件(Page):对应业务页面,如用户管理、订单列表等。
- 通用组件(Component):可复用的 UI 组件,如分页、表单、弹窗等。
- 路由(Router):按业务模块分层,如 /system(系统管理)、/business(业务模块)。
- 请求封装(Axios):统一处理请求拦截、响应拦截、错误处理。
- 状态管理(Pinia):存储全局状态,如用户信息、菜单权限等。
3. 数据库设计:规范先行,预留扩展
MySQL 数据库设计需遵循三大范式,同时兼顾性能:
- 表名、字段名采用小写 + 下划线命名,如
sys_user、user_name。 - 核心表添加主键(自增 / 雪花算法)、创建时间、更新时间、删除标记(逻辑删除)字段。
- 针对高频查询字段建立索引,避免过度索引。
二、核心技术实现:代码实例落地
接下来,通过具体的代码实例,讲解从后端接口开发到前端调用的完整流程,以用户管理模块为例。
环境准备
- 后端:Spring Boot 3.2.0、MyBatis-Plus 3.5.5、MySQL 8.0、JDK 17
- 前端:Vue3、Vite、Element Plus、Axios、Pinia
1. 后端实现:用户 CRUD 接口开发
(1)数据库表设计
sql
CREATE TABLE `sys_user` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`user_name` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(100) NOT NULL COMMENT '密码(加密)',
`nick_name` varchar(50) DEFAULT NULL COMMENT '昵称',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`is_deleted` tinyint DEFAULT 0 COMMENT '删除标记(0:未删,1:已删)',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_name` (`user_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户表';
(2)后端代码实现
① 实体类(SysUser.java)
java
运行
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("sys_user")
public class SysUser {
/**
* 主键ID
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 用户名
*/
private String userName;
/**
* 密码(加密)
*/
private String password;
/**
* 昵称
*/
private String nickName;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
/**
* 删除标记
*/
@TableLogic
private Integer isDeleted;
}
② Mapper 层(SysUserMapper.java)
java
运行
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.SysUser;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {
}
③ Service 层(SysUserService.java + 实现类)
java
运行
// 接口
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.entity.SysUser;
public interface SysUserService extends IService<SysUser> {
}
// 实现类
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.entity.SysUser;
import com.example.demo.mapper.SysUserMapper;
import com.example.demo.service.SysUserService;
import org.springframework.stereotype.Service;
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
}
④ 统一返回结果封装(Result.java)
java
运行
import lombok.Data;
@Data
public class Result<T> {
/**
* 状态码:200成功,500失败
*/
private Integer code;
/**
* 提示信息
*/
private String msg;
/**
* 数据
*/
private T data;
// 成功响应
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMsg("操作成功");
result.setData(data);
return result;
}
// 失败响应
public static <T> Result<T> error(String msg) {
Result<T> result = new Result<>();
result.setCode(500);
result.setMsg(msg);
result.setData(null);
return result;
}
}
⑤ Controller 层(SysUserController.java)
java
运行
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.example.demo.entity.SysUser;
import com.example.demo.result.Result;
import com.example.demo.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/sys/user")
@CrossOrigin // 跨域支持,生产环境建议统一配置
public class SysUserController {
@Autowired
private SysUserService sysUserService;
/**
* 新增用户
*/
@PostMapping
public Result<Boolean> save(@RequestBody SysUser sysUser) {
// 实际项目中需添加密码加密逻辑,如BCrypt加密
return Result.success(sysUserService.save(sysUser));
}
/**
* 根据ID删除用户
*/
@DeleteMapping("/{id}")
public Result<Boolean> delete(@PathVariable Long id) {
return Result.success(sysUserService.removeById(id));
}
/**
* 修改用户
*/
@PutMapping
public Result<Boolean> update(@RequestBody SysUser sysUser) {
return Result.success(sysUserService.updateById(sysUser));
}
/**
* 根据ID查询用户
*/
@GetMapping("/{id}")
public Result<SysUser> getById(@PathVariable Long id) {
return Result.success(sysUserService.getById(id));
}
/**
* 查询所有用户
*/
@GetMapping("/list")
public Result<List<SysUser>> list() {
LambdaQueryWrapper<SysUser> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.orderByDesc(SysUser::getCreateTime);
return Result.success(sysUserService.list(queryWrapper));
}
}
2. 前端实现:用户管理页面开发
(1)Axios 请求封装(request.js)
javascript
运行
import axios from 'axios';
// 创建axios实例
const service = axios.create({
baseURL: 'http://localhost:8080', // 后端接口地址
timeout: 5000 // 请求超时时间
});
// 请求拦截器
service.interceptors.request.use(
(config) => {
// 可添加token请求头,如config.headers['Token'] = localStorage.getItem('token');
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use(
(response) => {
const res = response.data;
// 统一处理响应结果
if (res.code !== 200) {
ElMessage.error(res.msg || '请求失败');
return Promise.reject(res);
} else {
return res;
}
},
(error) => {
ElMessage.error(error.message || '服务器错误');
return Promise.reject(error);
}
);
export default service;
(2)用户 API 封装(userApi.js)
javascript
运行
import request from './request';
// 用户管理相关API
export const userApi = {
// 新增用户
saveUser: (data) => request.post('/sys/user', data),
// 删除用户
deleteUser: (id) => request.delete(`/sys/user/${id}`),
// 修改用户
updateUser: (data) => request.put('/sys/user', data),
// 根据ID查询用户
getUserById: (id) => request.get(`/sys/user/${id}`),
// 查询用户列表
getUserList: () => request.get('/sys/user/list')
};
(3)用户管理页面(UserManage.vue)
vue
<template>
<div class="user-manage">
<!-- 搜索与新增按钮 -->
<div class="search-bar">
<el-button type="primary" @click="openAddDialog">新增用户</el-button>
</div>
<!-- 用户列表 -->
<el-table :data="userList" border stripe style="width: 100%; margin-top: 10px">
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="userName" label="用户名" width="120" />
<el-table-column prop="nickName" label="昵称" width="120" />
<el-table-column prop="createTime" label="创建时间" width="200" />
<el-table-column label="操作" width="200">
<template #default="scope">
<el-button type="primary" size="small" @click="openEditDialog(scope.row)">编辑</el-button>
<el-button type="danger" size="small" @click="deleteUser(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 新增/编辑用户弹窗 -->
<el-dialog v-model="dialogVisible" title="用户信息" width="400px">
<el-form :model="userForm" label-width="80px" ref="userFormRef">
<el-form-item label="用户名" prop="userName" :rules="[{ required: true, message: '请输入用户名', trigger: 'blur' }]">
<el-input v-model="userForm.userName" />
</el-form-item>
<el-form-item label="密码" prop="password" v-if="isAdd" :rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]">
<el-input v-model="userForm.password" type="password" />
</el-form-item>
<el-form-item label="昵称" prop="nickName">
<el-input v-model="userForm.nickName" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted, reactive } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { userApi } from '@/api/userApi';
// 响应式数据
const userList = ref([]);
const dialogVisible = ref(false);
const isAdd = ref(true); // 是否为新增操作
const userFormRef = ref(null);
const userForm = reactive({
id: '',
userName: '',
password: '',
nickName: ''
});
// 页面加载时查询用户列表
onMounted(() => {
getUserList();
});
// 查询用户列表
const getUserList = async () => {
try {
const res = await userApi.getUserList();
userList.value = res.data;
} catch (error) {
console.error(error);
}
};
// 打开新增弹窗
const openAddDialog = () => {
resetForm();
isAdd.value = true;
dialogVisible.value = true;
};
// 打开编辑弹窗
const openEditDialog = (row) => {
resetForm();
isAdd.value = false;
// 赋值给表单
Object.assign(userForm, row);
dialogVisible.value = true;
};
// 重置表单
const resetForm = () => {
userFormRef.value?.resetFields();
Object.assign(userForm, {
id: '',
userName: '',
password: '',
nickName: ''
});
};
// 提交表单(新增/编辑)
const submitForm = async () => {
try {
await userFormRef.value.validate();
if (isAdd.value) {
await userApi.saveUser(userForm);
ElMessage.success('新增成功');
} else {
await userApi.updateUser(userForm);
ElMessage.success('编辑成功');
}
dialogVisible.value = false;
getUserList(); // 刷新列表
} catch (error) {
console.error(error);
}
};
// 删除用户
const deleteUser = async (id) => {
try {
await ElMessageBox.confirm('确定要删除该用户吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
await userApi.deleteUser(id);
ElMessage.success('删除成功');
getUserList(); // 刷新列表
} catch (error) {
ElMessage.info('已取消删除');
}
};
</script>
<style scoped>
.user-manage {
padding: 20px;
}
.search-bar {
display: flex;
justify-content: flex-end;
}
</style>
三、实战优化技巧:让项目更稳定、更高效
在实际项目开发中,光完成功能还不够,还需要考虑性能、安全、可维护性等问题。以下是我总结的几个关键优化技巧:
1. 后端优化
(1)密码加密存储
上述代码中密码是明文存储,实际项目中必须加密。可使用 Spring Security 的 BCrypt 加密:
java
运行
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
// 加密
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String encryptPassword = encoder.encode("123456");
// 验证
boolean matches = encoder.matches("123456", encryptPassword);
(2)统一跨域配置
单个 Controller 添加 @CrossOrigin 不够优雅,可在后端添加全局跨域配置:
java
运行
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOriginPattern("*"); // 允许所有域名(生产环境指定具体域名)
config.addAllowedHeader("*"); // 允许所有请求头
config.addAllowedMethod("*"); // 允许所有请求方法
config.setAllowCredentials(true); // 允许携带cookie
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
(3)MyBatis-Plus 分页插件
查询列表时,分页是刚需,添加 MyBatis-Plus 分页插件:
java
运行
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
2. 前端优化
(1)路由懒加载
减少首屏加载时间,Vue 路由使用懒加载:
javascript
运行
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/user',
name: 'user',
component: () => import('@/views/UserManage.vue') // 懒加载
}
]
});
export default router;
(2)表单数据校验增强
前端表单校验可添加更多规则,如用户名长度、密码复杂度等:
javascript
运行
// 示例:用户名校验规则
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '用户名长度在3-20个字符之间', trigger: 'blur' }
3. 数据库优化
(1)分页查询
避免使用SELECT *和全表查询,使用 MyBatis-Plus 的分页查询:
java
运行
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 分页查询
Page<SysUser> page = new Page<>(1, 10); // 第1页,每页10条
IPage<SysUser> userPage = sysUserService.page(page, queryWrapper);
List<SysUser> records = userPage.getRecords(); // 数据列表
long total = userPage.getTotal(); // 总条数
(2)逻辑删除
使用 MyBatis-Plus 的逻辑删除,避免物理删除数据,便于数据恢复。
四、总结
Spring Boot+Vue+MySQL 是目前非常主流的全栈开发技术组合,其优势在于开发效率高、生态完善、社区活跃。在开发过程中,架构设计要注重解耦,代码实现要注重规范,功能完成后要注重优化。本文通过用户管理模块的实战案例,展示了从后端接口到前端页面的完整开发流程,并分享了一些优化技巧。
当然,实际项目中还会涉及更多复杂场景,如用户认证(JWT)、权限管理(RBAC)、Redis 缓存、文件上传等,这些内容我会在后续博客中逐一分享。如果本文对你有帮助,欢迎点赞、收藏、评论交流!




1377

被折叠的 条评论
为什么被折叠?



