本文使用 Dockerfile 和 Compose 组织 Vue 3 静态前端与 Spring Boot 后端。先构建各自镜像,再配置容器网络、反向代理和回环端口,最后分别验证页面与接口。
场景与技术选型
核心需求
- 技术栈:前端 Vue3(Vite 构建) + 后端 SpringBoot 3.x;
- 部署方式:Docker 容器化,环境完全隔离;
- 网络安全:仅绑定回环地址,避免外网访问;
- 运维便捷:批量启动 / 停止,自动处理依赖和网络。
前置准备
- 服务器 / 本地环境已安装 Docker 和 Docker Compose;
- 已完成 Vue3 前端项目和 SpringBoot 后端项目开发;
- 基础的 Docker 命令认知(无需深入,本文全程复制即用)。
项目目录结构
先梳理标准化的项目结构,后续所有配置均基于此,建议按此目录组织代码:
vue3-springboot-demo/
├── frontend/ # Vue3前端项目(Vite构建)
│ ├── Dockerfile # 前端镜像构建文件
│ ├── package.json # 前端依赖
│ ├── vite.config.js # Vite配置
│ └── dist/ # 打包后的静态文件(npm run build生成)
├── backend/ # SpringBoot后端项目
│ ├── Dockerfile # 后端镜像构建文件
│ ├── pom.xml # Maven依赖
│ └── src/ # 后端源码
└── docker-compose.yml # Compose编排文件(核心)编写 Dockerfile(前后端镜像构建)
前端 Dockerfile(Vue3 + Vite)
路径:frontend/Dockerfile
# 阶段1:构建前端静态文件
FROM node:24-alpine as build-stage
WORKDIR /app
# 复制依赖文件(利用Docker缓存,避免每次都重装依赖)
COPY package*.json ./
# 国内镜像源加速安装
RUN npm install --registry=https://registry.npmmirror.com
# 复制全部源码
COPY . .
# 打包构建(Vite构建生产环境包)
RUN npm run build
# 阶段2:使用Nginx部署静态文件(轻量、高性能)
FROM nginx:alpine as production-stage
# 复制构建好的静态文件到Nginx默认目录
COPY --from=build-stage /app/dist /usr/share/nginx/html
# 自定义Nginx配置(解决Vue Router history模式刷新404 + 接口反向代理)
COPY nginx.conf /etc/nginx/conf.d/default.conf
# 暴露容器内部80端口(仅容器内可见)
EXPOSE 80
# 启动Nginx(前台运行,避免容器退出)
CMD ["nginx", "-g", "daemon off;"]补充:前端 Nginx 配置(关键)
创建frontend/nginx.conf,解决路由问题并代理后端接口:
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# 解决Vue3 Router history模式刷新404
location / {
try_files $uri $uri/ /index.html;
}
# 反向代理后端接口(前端请求/api全部转发到后端容器)
location /api {
proxy_pass http://backend:8080; # backend是Compose中后端服务名
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 静态资源缓存(优化性能)
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 7d;
add_header Cache-Control "public, max-age=604800";
}
}后端 Dockerfile(SpringBoot 3.x)
路径:backend/Dockerfile(采用多阶段构建,减小镜像体积)
# 阶段1:构建SpringBoot项目
FROM maven:3.8.5-openjdk-17 as build-stage
WORKDIR /app
# 复制pom.xml和源码
COPY pom.xml ./
COPY src ./src
# 国内镜像源加速打包(替换为阿里云Maven仓库)
RUN mvn clean package -Dmaven.test.skip=true -Dmaven.repo.local=/root/.m2/repository -s /usr/share/maven/ref/settings-docker.xml
# 阶段2:运行SpringBoot项目(轻量JRE镜像)
FROM openjdk:17-jdk-slim
WORKDIR /app
# 从构建阶段复制打包好的jar包
COPY --from=build-stage /app/target/*.jar app.jar
# 暴露后端端口
EXPOSE 8080
# JVM优化参数(可选)
ENV JAVA_OPTS="-Xms512m -Xmx1024m -Djava.security.egd=file:/dev/./urandom"
# 启动后端服务
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]后端示例接口(测试用)
在 SpringBoot 项目中编写一个简单接口,用于验证前后端通信:
// com.example.demo.controller.HelloController
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api")
public class HelloController {
@GetMapping("/hello")
public Map<String, Object> hello() {
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("message", "Hello from SpringBoot Backend!");
result.put("data", "Vue3 + SpringBoot 部署成功");
return result;
}
}SpringBoot 跨域配置(可选)
若未使用 Nginx 代理,需在后端添加跨域配置:
// com.example.demo.config.CorsConfig
package com.example.demo.config;
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.addAllowedOrigin("http://127.0.0.1:8080");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", config);
return new CorsFilter(source);
}
}编写 Docker Compose 文件(批量编排)
路径:docker-compose.yml(核心配置,实现批量启动)
# 定义服务集合
services:
# 前端服务
frontend:
# 基于前端目录的Dockerfile构建镜像
build: ./frontend
# 容器名称(便于管理)
container_name: vue3-frontend
# 重启策略:异常崩溃/服务器重启后自动启动(除非手动停止)
restart: unless-stopped
# 端口映射:仅绑定回环地址127.0.0.1,主机8080 → 容器80
# 关键:127.0.0.1限定仅本机可访问,外网无法连接
ports:
- "127.0.0.1:8080:80"
# 依赖后端服务,确保后端先启动
depends_on:
- backend
# 加入自定义网络(前后端容器互通)
networks:
- app-network
# 后端服务
backend:
# 基于后端目录的Dockerfile构建镜像
build: ./backend
container_name: springboot-backend
restart: unless-stopped
# 端口映射:仅绑定回环地址,主机8081 → 容器8080
ports:
- "127.0.0.1:8081:8080"
# 环境变量(可配置数据库地址、端口等)
environment:
- SPRING_PROFILES_ACTIVE=prod
- SERVER_PORT=8080
# 自定义网络
networks:
- app-network
# 自定义桥接网络(隔离外部网络,仅前后端容器互通)
networks:
app-network:
driver: bridge核心配置说明
-
端口映射规则:
127.0.0.1:主机端口:容器端口例如
127.0.0.1:8080:80表示:仅本机可通过 8080 端口访问前端容器的 80 端口,外网无法访问,极大提升安全性。 -
depends_on:保证后端服务先启动,避免前端请求时后端未就绪。
-
自定义网络:
app-network让前后端容器通过服务名(如backend)互通,无需手动配置 IP。
批量启动与运维命令
启动服务(核心命令)
在vue3-springboot-demo根目录下执行:
# 首次启动:构建镜像 + 启动容器(后台运行)
docker-compose up -d --build
# 后续启动(无需重新构建镜像)
docker-compose up -d-d:后台运行容器,不占用终端;--build:强制重新构建镜像(代码修改后需加此参数)。
验证服务是否正常
# 查看容器运行状态
docker-compose ps
# 查看前端日志(排查前端问题)
docker-compose logs -f frontend
# 查看后端日志(排查后端问题)
docker-compose logs -f backend
# 测试后端接口(本机访问)
curl http://127.0.0.1:8081/api/hello若返回如下 JSON,说明后端正常:
{"code":200,"data":"Vue3 + SpringBoot 部署成功","message":"Hello from SpringBoot Backend!"}前端访问:打开浏览器输入 http://127.0.0.1:8080,即可看到 Vue3 页面,且能正常调用后端接口。
常用运维命令
# 停止服务(保留容器,可重启)
docker-compose stop
# 重启服务
docker-compose restart
# 停止并删除容器(保留镜像)
docker-compose down
# 停止并删除容器 + 镜像(彻底清理)
docker-compose down --rmi all
# 查看容器资源占用
docker stats vue3-frontend springboot-backend常见问题排查
前端无法访问后端接口?
- 检查 Nginx 配置中的
proxy_pass是否为http://backend:8080(后端服务名 + 容器端口); - 确认后端服务监听地址为
0.0.0.0(SpringBoot 默认就是,无需修改); - 查看前端容器日志:
docker-compose logs frontend,检查 Nginx 是否启动正常。
构建镜像时依赖下载慢?
- 前端:在
package.json中使用国内 npm 镜像(registry=https://registry.npmmirror.com); - 后端:在
pom.xml中添加阿里云 Maven 镜像源。
端口被占用?
修改docker-compose.yml中的主机端口,例如将127.0.0.1:8080:80改为127.0.0.1:8082:80。
总结
- 通过
Dockerfile实现 Vue3/SpringBoot 的镜像标准化构建,解决了 “环境不一致” 的经典问题; docker-compose.yml实现批量编排,自动处理容器依赖、网络、端口映射,大幅降低部署复杂度;- 端口映射绑定
127.0.0.1,仅允许本机访问,在开发 / 测试环境中兼顾便捷性与安全性。
这套方案适用于本地开发、内网测试、小型个人项目部署,无需手动配置 Nginx、JDK、Node.js 等环境,真正做到 “一次编写,到处运行”。
启动顺序与业务就绪
Compose 的 depends_on 简写只约束启动顺序。后端进程启动后还可能等待数据库迁移或连接池初始化,需要应用重试及可反映业务就绪的 healthcheck;使用 service_healthy 条件前应确保检查命令在镜像中存在。规则见 Compose 启动顺序。
前端通过同源 /api 路径请求时,浏览器不需要知道 Compose 服务名。服务名 backend 由容器网络解析,不能直接作为用户浏览器地址。构建通过后分别验证静态页面、history 深层路由、API 成功和后端不可用四种情况。
← 返回文章