
1. 为什么选择SpringBootVue前后端分离架构在2018年之前我参与的企业级项目大多采用传统的JSPServlet模式。每次修改前端页面都需要重新编译打包整个项目一个简单的CSS调整可能要让后端同事等待5分钟以上的构建时间。直到我们团队接手某政务云平台项目时首次尝试了SpringBootVue的前后端分离架构开发效率提升了近3倍。前后端分离的核心价值在于解耦。SpringBoot负责提供RESTful API接口处理业务逻辑和数据持久化Vue则专注于页面渲染和用户交互。这种架构下前端开发者可以独立运行npm run dev启动热更新开发服务器修改代码后浏览器实时刷新后端开发者则专注于接口设计和性能优化双方通过Swagger文档定义接口规范并行开发互不干扰。我最近为某跨境电商平台搭建的监控系统就采用了这套架构。SpringBoot 3.1.5处理日均2000万条日志分析Vue 3组合式API实现实时数据可视化。当需要调整仪表盘布局时前端团队可以在不中断后端服务的情况下完成迭代这是传统架构无法比拟的优势。2. 技术栈选型与版本搭配建议2.1 SpringBoot后端技术栈在最近的项目中我推荐使用以下稳定组合SpringBoot 3.1.5要求JDK17MyBatis-Plus 3.5.3.1简化CRUD操作PageHelper 1.4.6分页插件Hutool 5.8.21工具类库Knife4j 4.3.0API文档增强特别注意版本兼容性MyBatis-Plus 3.5.x需要配合SpringBoot 3.x使用。去年有个项目因为混用SpringBoot 2.7 MyBatis-Plus 3.5.1导致自动注入失效排查了整整两天才发现是版本冲突。2.2 Vue前端技术栈经过多个项目验证这套组合最稳定Vue 3.3.4组合式APIPinia 2.1.3状态管理Element Plus 2.3.14UI组件库Axios 1.4.0HTTP客户端Vue Router 4.2.4路由管理近期有个坑需要注意Vue 3.3要求Node.js版本≥16.11.0。有次在阿里云效上构建失败就是因为默认的Node.js 14.x不兼容。3. 项目初始化与工程结构3.1 后端工程搭建使用Spring Initializr创建项目时我通常会勾选Spring WebWeb MVC支持Lombok简化POJOMyBatis Framework数据库访问MySQL Driver数据库驱动建议的包结构src/main/java ├── com.xxx │ ├── config # 配置类 │ ├── controller # 控制器 │ ├── service # 服务层 │ ├── mapper # MyBatis接口 │ ├── entity # 实体类 │ └── util # 工具类 src/main/resources ├── application.yml # 主配置 ├── mapper # XML文件 └── static # 静态资源3.2 前端工程初始化推荐使用Vite创建项目比Webpack快10倍npm create vitelatest frontend --template vue cd frontend npm install element-plus axios vue-router pinia我的典型目录结构src ├── api # 接口定义 ├── assets # 静态资源 ├── components # 公共组件 ├── composables # 组合式函数 ├── router # 路由配置 ├── stores # Pinia状态库 ├── utils # 工具函数 └── views # 页面组件4. 前后端联调关键配置4.1 解决跨域问题在SpringBoot中添加配置类Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOrigins(http://localhost:5173) // Vue开发服务器端口 .allowedMethods(*) .allowCredentials(true); } }生产环境建议通过Nginx反向代理解决跨域server { listen 80; server_name yourdomain.com; location /api { proxy_pass http://backend:8080; proxy_set_header Host $host; } location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } }4.2 接口规范设计我习惯使用RESTful风格响应体统一格式public class ResultT { private Integer code; private String msg; private T data; // 成功响应工厂方法 public static T ResultT success(T data) { return new Result(200, success, data); } }前端封装axios实例const service axios.create({ baseURL: import.meta.env.VITE_API_URL, timeout: 10000 }) // 请求拦截器 service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) // 响应拦截器 service.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.msg || Error) return Promise.reject(new Error(res.msg || Error)) } return res.data } )5. 权限控制方案实现5.1 后端安全配置使用Spring Security JWT方案Configuration EnableWebSecurity public class SecurityConfig { Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() .antMatchers(/api/auth/login).permitAll() .anyRequest().authenticated() .and() .addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } Bean public JwtFilter jwtFilter() { return new JwtFilter(); } }JWT过滤器核心逻辑String token request.getHeader(Authorization); if (token ! null token.startsWith(Bearer )) { token token.substring(7); try { String username Jwts.parserBuilder() .setSigningKey(key) .build() .parseClaimsJws(token) .getBody() .getSubject(); UserDetails user userService.loadUserByUsername(username); UsernamePasswordAuthenticationToken auth new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(auth); } catch (JwtException e) { throw new AuthenticationServiceException(Invalid token); } }5.2 前端路由守卫在Vue Router中实现权限控制router.beforeEach(async (to) { const token localStorage.getItem(token) // 需要登录但未登录 if (to.meta.requiresAuth !token) { return { path: /login, query: { redirect: to.fullPath } } } // 已登录但访问登录页 if (token to.path /login) { return { path: / } } // 动态路由处理 if (token !hasRoutes) { const { menus } await getUserInfo() addDynamicRoutes(menus) return to.fullPath } })6. 生产环境部署实践6.1 后端打包优化在application.yml中区分环境配置spring: profiles: active: profileActive --- spring: profiles: dev datasource: url: jdbc:mysql://localhost:3306/test --- spring: profiles: prod datasource: url: jdbc:mysql://prod-db:3306/prod使用Maven多环境打包mvn clean package -Pprod -DskipTests6.2 前端性能优化vite.config.js生产配置export default defineConfig({ build: { rollupOptions: { output: { manualChunks(id) { if (id.includes(node_modules)) { return vendor } } } }, chunkSizeWarningLimit: 1000 }, plugins: [ vitePluginCompression({ threshold: 10240 // 对大于10KB的文件进行gzip压缩 }) ] })6.3 Docker容器化部署后端Dockerfile示例FROM eclipse-temurin:17-jdk-alpine VOLUME /tmp COPY target/*.jar app.jar ENTRYPOINT [java,-jar,/app.jar]前端Dockerfile示例FROM nginx:alpine COPY dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD [nginx, -g, daemon off;]使用docker-compose编排version: 3 services: frontend: image: vue-app ports: - 80:80 depends_on: - backend backend: image: springboot-app environment: - SPRING_PROFILES_ACTIVEprod ports: - 8080:80807. 常见问题排查指南7.1 接口404问题排查流程检查后端控制台是否打印出映射路径使用Postman直接测试后端接口查看浏览器开发者工具中的Network面板确认Nginx配置是否正确转发请求检查SpringBoot的RequestMapping注解路径7.2 Vue页面刷新白屏问题这是SPA应用的经典问题解决方案location / { try_files $uri $uri/ /index.html; }7.3 MyBatis映射文件加载失败确保application.yml配置了mapper路径mybatis: mapper-locations: classpath:mapper/*.xml并在启动类添加MapperScan注解MapperScan(com.xxx.mapper) SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }8. 项目优化进阶技巧8.1 接口性能监控集成Prometheus GrafanaBean public MeterRegistryCustomizerPrometheusMeterRegistry metricsCommonTags() { return registry - registry.config().commonTags(application, springboot-vue-demo); }8.2 前端长列表优化使用vue-virtual-scrollerRecycleScroller classscroller :itemslist :item-size54 key-fieldid template #default{ item } div classitem{{ item.name }}/div /template /RecycleScroller8.3 后端缓存策略Redis缓存示例Cacheable(value users, key #id) public User getUserById(Long id) { return userMapper.selectById(id); } CacheEvict(value users, key #user.id) public void updateUser(User user) { userMapper.updateById(user); }在最近的一个高并发项目中通过二级缓存本地缓存组合QPS从200提升到了1500。关键是要做好缓存穿透和雪崩防护Cacheable(value users, key #id, unless #result null, cacheManager redisCacheManager) public User getWithProtection(Long id) { // 布隆过滤器先判断是否存在 if (!bloomFilter.mightContain(id)) { return null; } return userMapper.selectById(id); }9. 前后端协作规范建议9.1 接口文档管理使用Swagger YAPI的方案Configuration EnableOpenApi public class SwaggerConfig { Bean public Docket api() { return new Docket(DocumentationType.OAS_30) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage(com.xxx.controller)) .paths(PathSelectors.any()) .build(); } }9.2 代码风格统一后端.editorconfig配置[*.java] indent_style space indent_size 4 charset utf-8 trim_trailing_whitespace true insert_final_newline true前端.eslintrc.js配置module.exports { rules: { vue/multi-word-component-names: off, vue/html-indent: [error, 2], vue/script-indent: [error, 2, { baseIndent: 1 }] } }9.3 Git分支策略我们团队采用的分支模型main - 生产环境代码保护分支 release/* - 预发布分支 develop - 集成测试分支 feature/* - 功能开发分支 hotfix/* - 紧急修复分支配合Git Flow工作流# 新功能开发 git checkout -b feature/user-auth develop # 合并到开发分支 git checkout develop git merge --no-ff feature/user-auth git branch -d feature/user-auth10. 项目扩展方向10.1 微服务化改造逐步演进为Spring Cloud架构注册中心Nacos配置中心Nacos Config服务网关Spring Cloud Gateway服务调用OpenFeign熔断降级Sentinel10.2 低代码平台集成集成amis可视化编辑器template AMIS :schemaschema / /template script setup import AMIS from amis-vue const schema { type: page, title: 用户表单, body: { type: form, api: /api/user, controls: [ {type: text, name: name, label: 姓名} ] } } /script10.3 移动端适配方案使用vwrem方案// 基准375px(iPhone6) function vw($px) { return ($px / 375) * 100vw; } html { font-size: vw(16); }或者直接使用Vant移动端组件库npm install vantlatest-v3在项目中按需引入import { Button, Cell } from vant app.use(Button).use(Cell)