Spring Boot+Vue+MySQL 全栈开发技术实施技巧与实战
在当今 Web 应用开发领域,Spring Boot、Vue、MySQL 的组合凭借其高效、灵活的特性,成为众多开发者的首选方案。然而,从技术选型到项目落地,过程中往往会遇到环境配置冲突、接口联调不畅、性能瓶颈等问题。本文将结合实际开发经验,从环境搭建、核心功能实现、性能优化、部署运维四个维度,分享全栈开发中的实用技巧与可落地的代码实例,帮助开发者少走弯路,提升项目开发效率与质量。
一、环境搭建:避坑指南与标准化配置
环境搭建是项目开发的基础,若配置不规范,后续会频繁出现兼容性问题。以下是经过实战验证的标准化配置方案,适用于大多数中小型 Web 项目。
1.1 后端 Spring Boot 环境(JDK 17 + Maven 3.9.6)
核心依赖管理(pom.xml 关键配置)
>
.springframework.boot -starter-parent
.2.0> 兼容JDK17 -->
</parent>
<dependencies>
Web核心依赖 -->
<dependency>
<groupId>org.springframework.boot>
spring-boot-starter-web</artifactId>
>
(适配8.0+版本) -->
<dependency>
>com.mysql>
mysql-connector-j>
runtime </dependency>
MyBatis-Plus(简化CRUD,提升开发效率) -->
<dependency>
>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter 5.5
参数合法性检查) -->
<groupId>org.springframework.boot</groupId>
>spring-boot-starter-validation </dependency>
跨域与JSON处理 -->
.alibaba 2 .44
</dependencies>
Maven编译配置(指定JDK版本) -->
<build>
>
.boot aven-plugin <source>17</source>
>17
</plugins>
>
避坑技巧:
- MySQL 版本适配:若使用 MySQL 5.7,需将驱动改为mysql-connector-java,版本指定为5.1.49,同时 URL 添加useSSL=false&serverTimezone=UTC;
- Maven 依赖冲突:通过mvn dependency:tree命令查看依赖树,排除冲突依赖(如 Spring Boot 自带的jackson与fastjson2冲突时,需排除jackson);
- 端口占用问题:在application.yml中配置server.port=8081(自定义端口),避免与前端 Vue 或其他服务冲突。
1.2 前端 Vue 环境(Vue 3 + Vite + Axios)
初始化与核心依赖安装
# 初始化Vue项目(使用Vite,比Vue CLI更快)
npm create vite@latest my-vue-app -- --template vue-ts
# 进入项目目录
cd my-vue-app
# 安装核心依赖
npm install axios@1.6.2 vue-router@4.2.5 pinia@2.1.7 element-plus@2.5.1
全局配置(main.ts)
import { createApp } from 'vue'
import App from './App.vue'
import router from './router' // 路由配置
import { createPinia } from 'pinia' // 状态管理
import ElementPlus from 'element-plus' // UI组件库
import 'element-plus/dist/index.css'
import axios from './utils/axios' // 封装后的Axios
const app = createApp(App)
// 全局挂载
app.use(router)
app.use(createPinia())
app.use(ElementPlus)
app.config.globalProperties.$axios = axios
app.mount('#app')
避坑技巧:
- Vite 跨域配置:在vite.config.ts中配置代理,解决开发环境跨域问题:
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:8081', // 后端服务地址
rewrite: (path) => path.replace(/^\/api/, ''),
changeOrigin: true
}
}
}
})
- TypeScript 类型报错:Vue 3 推荐使用 TypeScript,若遇到 “类型 “undefined” 不能赋值给类型 XXX”,需在tsconfig.json中开启"strictNullChecks": false(开发初期可临时关闭,后期逐步优化类型)。
二、核心功能实现:从接口到前端联调
以 “用户管理模块” 为例,展示后端接口开发、前端页面实现的完整流程,包含参数校验、异常处理、数据渲染等关键环节。
2.1 后端接口开发(基于 MyBatis-Plus)
1. 实体类定义(User.java)
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
@TableName("sys_user") // 对应MySQL表名
public class User {
@TableId(type = IdType.AUTO) // 自增主键
private Long id;
@NotBlank(message = "用户名不能为空") // 数据校验
private String username;
@NotBlank(message = "密码不能为空")
private String password;
@Email(message = "邮箱格式不正确")
private String email;
private Integer status; // 1-启用,0-禁用
}
2. Mapper 接口(UserMapper.java)
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper // 标记为MyBatis Mapper接口
public interface UserMapper extends BaseMapper {
// 无需手动写SQL,BaseMapper已提供CRUD方法
}
3. 服务层实现(UserService.java + UserServiceImpl.java)
// UserService.java(接口)
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.entity.User;
public interface UserService extends IService> {
// 扩展自定义方法(如根据用户名查询)
User getByUsername(String username);
}
// UserServiceImpl.java(实现类)
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl, User> implements UserService {
@Override
public User getByUsername(String username) {
// 使用MyBatis-Plus的条件构造器
return lambdaQuery().eq(User::getUsername, username).one();
}
}
4. 控制器(UserController.java)
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/user") // 接口前缀,与前端代理对应
public class UserController {
@Autowired
private UserService userService;
// 1. 查询所有用户
@GetMapping
public ResponseEntity<ListUsers() {
List<User> users = userService.list();
return new ResponseEntity.OK);
}
// 2. 根据ID查询用户
@GetMapping("/{id}")
public ResponseEntity getUserById(@PathVariable Long id) {
User user = userService.getById(id);
return user != null ?
new ResponseEntity<>(user, HttpStatus.OK) :
new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// 3. 新增用户(参数校验)
@PostMapping
public ResponseEntity(@Valid @RequestBody User user) {
// 密码加密(后续章节会讲,此处先占位)
// user.setPassword(passwordEncoder.encode(user.getPassword()));
boolean success = userService.save(user);
return success ?
new ResponseEntity成功", HttpStatus.CREATED) :
new ResponseEntity HttpStatus.INTERNAL_SERVER_ERROR);
}
// 4. 修改用户
@PutMapping("/{id}")
public ResponseEntity(@PathVariable Long id, @Valid @RequestBody User user) {
user.setId(id); // 确保ID与路径一致
boolean success = userService.updateById(user);
return success ?
new ResponseEntity("修改成功", HttpStatus.OK) :
new ResponseEntity HttpStatus.INTERNAL_SERVER_ERROR);
}
// 5. 删除用户
@DeleteMapping("/{id}")
public ResponseEntity(@PathVariable Long id) {
boolean success = userService.removeById(id);
return success ?
new ResponseEntity", HttpStatus.OK) :
new ResponseEntity删除失败", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
2.2 前端页面实现(用户列表页)
1. 路由配置(router/index.ts)
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import UserList from '../views/UserList.vue'
const routes: RouteRecordRaw[] = [
{
path: '/user',
name: 'UserList',
component: UserList
}
]
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes
})
export default router
2. Axios 封装(utils/axios.ts)
import axios from 'axios'
const service = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API, // 环境变量(.env文件配置)
timeout: 5000
})
// 请求拦截器(添加Token等)
service.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => {
return Promise.reject(error)
}
)
// 响应拦截器(统一异常处理)
service.interceptors.response.use(
(response) => {
return response.data
},
(error) => {
// 处理401未授权、500服务器错误等
if (error.response?.status === 401) {
localStorage.removeItem('token')
window.location.href = '/login'
}
return Promise.reject(error)
}
)
export default service
3. 用户列表组件(views/UserList.vue)
<template>
class="container">
用户管理">>
按钮 -->
-button type="primary" @click="openAddDialog">新增用户
data="userList" border style="width: 100%; margin-top: 20px;">
="id" label="ID" align="center">-table-column>
<el-table-column prop="username" label="用户名" align="center">>
-column prop="email" label="邮箱" align="center"> ="status" label="状态" align="center">
scope">
-tag :type="scope.row.status === 1 ? 'success' : 'danger'">
{{ scope.row.status === 1 ? '启用' : '禁用' }}
>
>
-column label="操作" align="center">
<template #default="scope">
="text" @click="openEditDialog(scope.row)">编辑
="text" @click="deleteUser(scope.row.id)" style="color: #f56c6c;">删除
编辑对话框 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle">
-form :model="form" :rules="rules" ref="formRef" label-width="120px">
label="用户名" prop="username">
-input v-model="form.username"> ="密码" :required="isAdd">
<el-input v-model="form.password" type="password"></el-input>
</el-form-item>
邮箱" prop="email">
-model="form.email">-input>
-form-item>
<el-form-item label="状态" prop="status">
="form.status" placeholder="请选择">
<el-option label="启用" value="1">>
label="禁用" value="0">
>
@click="dialogVisible = false">取消>
type="primary" @click="submitForm">确定</el-button>
</template>
-dialog>
>
<script setup lang="ts">
import { ref, onMounted, reactive } from 'vue'
import { ElTable, ElButton, ElDialog, ElForm, ElFormItem, ElInput, ElSelect, ElOption, ElTag, ElPageHeader, ElMessage } from 'element-plus'
import axios from '@/utils/axios'
// 定义变量
const userList = refconst dialogVisible = ref(false)
const dialogTitle = ref('新增用户')
const isAdd = ref(true)
const formRef = ref<InstanceType ElForm>>()
const form = reactive({
id: '',
username: '',
password: '',
email: '',
status: '1'
})
const rules = reactive({
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
email: [{ required: true, message: '请输入邮箱', trigger: 'blur' }, { type: 'email', message: '邮箱格式不正确', trigger: 'blur' }],
status: [{ required: true, message: '请选择状态', trigger: 'change' }]
})
// 页面加载时查询用户列表
onMounted(() => {
getUsers()
})
// 查询所有用户
const getUsers = async () => {
try {
const res = await axios.get('/api/user')
userList.value = res
} catch (error) {
ElMessage.error('查询用户失败')
console.error(error)
}
}
// 打开新增对话框
const openAddDialog = () => {
dialogTitle.value = '新增用户'
isAdd.value = true
// 重置表单
formRef.value?.resetFields()
form.id = ''
form.password = ''
dialogVisible.value = true
}
// 打开编辑对话框
const openEditDialog = (row: any) => {
dialogTitle.value = '编辑用户'
isAdd.value = false
// 回显数据
form.id = row.id
form.username = row.username
form.email = row.email
form.status = row.status + '' // 转为字符串,适配下拉框
dialogVisible.value = true
}
// 提交表单(新增/编辑)
const submitForm = async () => {
try {
// 表单校验
更多推荐



所有评论(0)