🚀记录一下springboot vue的打包方式,第一种比较常用。
在采用 Spring Boot + Vue 技术栈进行开发时,前后端分离的架构是主流选择。这种架构在部署上有多种策略,每种策略都有其适用场景。以下是几种核心的部署方式,从简单到复杂,供您参考。
1️⃣ 策略一:前后端分离部署
这是最灵活、最常用的生产环境部署模式。前端和后端作为两个独立的服务,分别进行打包和部署。
⚙️ 后端部署 (Spring Boot)
- 构建应用: 使用 Maven 或 Gradle 将 Spring Boot 应用打包成可执行的 JAR 文件。
# 使用 Maven 打包,跳过测试 mvn clean package -DskipTests
- 运行应用: 将生成的
your-app.jar
文件上传到服务器,并通过 Java 命令运行。# 后端服务通常监听在 8080 端口 java -jar your-app.jar
🎨 前端部署 (Vue)
-
构建应用: 将 Vue 项目编译打包成静态资源(HTML, CSS, JS)。
# 生成 dist 目录 npm run build
-
托管静态资源: 使用 Nginx 作为 Web 服务器来托管
dist
目录中的文件。Nginx 配置示例 (
nginx.conf
)server { listen 80; # 监听 80 端口 server_name your-domain.com; # 你的域名 location / { root /path/to/your/vue/dist; # Vue 打包后 dist 目录的路径 index index.html; # 默认文件 try_files $uri $uri/ /index.html; # 解决 Vue Router 的 History 模式下刷新 404 的问题 } # [推荐] 配置反向代理,解决跨域问题 location /api { proxy_pass http://localhost:8080; # 将所有 /api 开头的请求转发到后端服务 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
💡 关键点:跨域问题 (CORS)
由于前后端服务部署在不同的源(域名或端口),浏览器会阻止前端直接调用后端 API。解决方案有两种:
- 后端配置 CORS: 在 Spring Boot 中使用
@CrossOrigin
注解或全局配置来允许特定来源的跨域请求。 - Nginx 反向代理 (推荐): 如上方案例所示,通过 Nginx 将 API 请求代理到后端服务,对浏览器而言,所有请求都来自同一个源,从而避免了跨域问题。
优点 (✅):
- 高度解耦: 前后端可以独立开发、测试、升级和扩容。
- 职责清晰: 前端专注于用户界面,后端专注于业务逻辑。
- 灵活性高: 可将前端部署到 CDN 以加速访问。
缺点 (❌):
- 部署稍复杂: 需要分别管理两个服务的部署和运维。
- 依赖网络: 前后端通信完全依赖网络调用。
2️⃣ 策略二:前后端合并部署
这种方式将前端的静态资源打包进 Spring Boot 项目中,最终只生成一个可部署的 JAR 文件。
📦 打包流程
- 构建前端: 在 Vue 项目中执行
npm run build
,生成dist
目录。 - 复制资源: 将
dist
目录下的所有文件复制到 Spring Boot 项目的src/main/resources/static/
目录下。- 原理: Spring Boot 默认会将
resources/static/
目录下的内容作为静态资源对外提供访问。
- 原理: Spring Boot 默认会将
- 构建后端: 正常使用
mvn clean package
打包 Spring Boot 应用。 - 部署运行: 运行生成的单个 JAR 文件,Spring Boot 内嵌的 Web 服务器(如 Tomcat)会同时提供 API 服务和前端页面。 Bash
java -jar your-app.jar
优点 (✅):
- 部署简单: 只需管理一个 JAR 包,部署流程极大简化。
- 无跨域问题: 前后端同源,天然避免了 CORS 问题。
- 管理方便: 前后端版本统一,便于小型项目或单人开发。
缺点 (❌):
- 紧密耦合: 前后端技术栈绑定,不利于独立演进。
- 包体积大: 前端资源会增大 JAR 包的体积,可能影响启动和部署速度。
- 更新不便: 任何前端的微小改动都需要重新打包和部署整个后端服务。
3️⃣ 策略三:容器化部署 (Docker)
这是一种现代化、标准化的部署方式,将前后端应用分别打包成独立的 Docker 镜像,实现环境隔离和轻松迁移。
🐳 容器化流程
-
后端容器化:
- 创建
Dockerfile
: Dockerfile# 使用一个精简的 Java 基础镜像 FROM openjdk:11-jre-slim # 将打包好的 JAR 文件复制到容器中 COPY target/your-app.jar /app.jar # 暴露端口 EXPOSE 8080 # 容器启动时执行的命令 CMD ["java", "-jar", "/app.jar"]
- 构建镜像:
docker build -t your-backend-image .
- 创建
-
前端容器化:
- 创建
Dockerfile
(使用 Nginx 托管): Dockerfile# 阶段一:构建 Vue 应用 FROM node:lts-alpine as build-stage WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build # 阶段二:使用 Nginx 提供服务 FROM nginx:stable-alpine # 从构建阶段复制打包好的文件 COPY --from=build-stage /app/dist /usr/share/nginx/html # [可选] 复制自定义的 Nginx 配置文件 # COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]
- 构建镜像:
docker build -t your-frontend-image .
- 创建
-
编排与运行 (Docker Compose)
使用 docker-compose.yml 可以一键启动和管理前后端两个容器。
version: '3.8' services: backend: image: your-backend-image ports: - "8080:8080" # 其他配置,如数据库连接、环境变量等 frontend: image: your-frontend-image ports: - "80:80" # 将容器的 80 端口映射到主机的 80 端口 depends_on: - backend # 确保后端服务先启动
- 一键启动:
docker-compose up -d
- 一键启动:
优点 (✅):
- 环境一致性: 彻底解决 "在我电脑上是好的" 问题。
- 易于扩展和迁移: 可以在任何支持 Docker 的环境中快速部署和水平扩展。
- 标准化: 是通往 CI/CD 和微服务架构的必经之路。
缺点 (❌):
- 学习成本: 需要掌握 Docker 和 Docker Compose 的基本知识。
- 资源占用: Docker 本身会占用一定的系统资源。
🌟 部署策略对比与选择建议
特性 | 分离部署 | 合并部署 | 容器化部署 (Docker) |
部署复杂度 | 中等 | 简单 👍 | 较高 (初次配置) |
耦合度 | 低 (解耦) 👍 | 高 (耦合) | 低 (解耦) 👍 |
灵活性/扩展性 | 高 👍 | 低 | 非常高 👍👍 |
跨域处理 | 需要 (Nginx/后端CORS) | 无需 👍 | 需要 (同分离部署) |
适用场景 | 大多数生产项目 | 敏捷开发、小型项目、内部系统 | 所有规模的项目,特别是中大型和生产环境 👍 |
💻 代码演练:三种部署方式
为了让理论更具体,我们通过一个简单的“Hello World”项目来实际演示这三种核心部署策略。
通用准备 (Prerequisites)
我们假设已有两个基础项目:
- 后端 (Spring Boot): 一个名为
ruoyi-system
的项目。 - 前端 (Vue): 一个标准的 Vue 项目。
后端 API 示例
在 Spring Boot 项目中,我们创建一个简单的 Controller 提供 API 接口。这个接口将在所有三个示例中被调用。
HelloController.java
:
@RestController
@RequestMapping("/api")
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello from Spring Boot! Welcome!";
}
}
示例 1:🚀 前后端分离部署
这是最灵活的生产模式,前后端各司其职。
⚙️ 后端操作
- 打包: 进入
ruoyi-system
项目根目录,执行打包命令。mvn clean package -DskipTests
- 部署与运行: 将
target/ruoyi-system.jar
上传到服务器,并启动。java -jar ruoyi-system.jar # 后端服务现在运行在 http://localhost:8080
🎨 前端操作
-
修改 API 请求代码 (关键)
为了让生产环境的 Nginx 代理生效,前端代码中应使用相对路径来请求 API,而不是写死的 localhost。
代码段src/App.vue
:<template> <div id="app"> <h1>{{ message }}</h1> <button @click="fetchMessage">Get Message</button> </div> </template> <script> export default { data() { return { message: 'Click the button to fetch data.' }; }, methods: { fetchMessage() { // 使用相对路径,Nginx 会捕获 /api 并转发 fetch('/api/hello') .then(res => res.text()) .then(data => { this.message = data; }) .catch(err => { this.message = 'Failed to fetch: ' + err; }); } } }; </script>
-
打包:
npm run build
,生成dist
目录。 -
部署: 将
dist
目录内容上传到服务器,并使用 Nginx 托管。 -
配置 Nginx:
Nginx 的配置是连接前后端的桥梁。
Nginxnginx.conf
示例:server { listen 80; # 前端对外暴露的端口 server_name your_domain.com; # 托管前端静态文件 location / { root /path/to/vue/dist; try_files $uri $uri/ /index.html; } # 将所有 /api 请求反向代理到后端服务 location /api { proxy_pass http://localhost:8080; # 后端服务的地址 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }
当用户访问页面并点击按钮时,浏览器请求
http://your_domain.com/api/hello
,Nginx 接收到请求后,发现它匹配/api
路径,于是将请求无缝转发给http://localhost:8080/api/hello
。
示例 2:📦 前后端合并部署
将前端打包成静态资源,塞入 Spring Boot 项目中,实现单体部署。
⚙️ 后端与前端集成操作
-
前端打包: 在 Vue 项目中执行
npm run build
。 -
修改前端代码: 与分离部署一样,API 请求也应使用相对路径,因为最终前后端在同一个源下。
fetch('/api/hello')
的写法在这里同样适用。 -
复制文件: 将 Vue 打包生成的
dist/
目录下的所有内容复制到 Spring Boot 项目的src/main/resources/static/
目录下。 -
打包 Spring Boot:
mvn clean package -DskipTests
现在生成的
ruoyi-system.jar
文件同时包含了后端代码和所有前端静态资源。
🚀 部署
只需将这个增强版的 JAR 文件上传到服务器并运行即可。
java -jar ruoyi-system.jar
现在,访问 http://your_server_ip:8080
,你将直接看到 Vue 应用界面。点击按钮,请求 http://your_server_ip:8080/api/hello
会被同一个 Spring Boot 服务处理,无需任何代理和跨域配置。
示例 3:🐳 Docker 容器化部署
使用 Docker Compose 编排前后端两个容器,实现优雅、隔离的部署。
📁 推荐项目结构
my-project/
├── backend/ # Spring Boot 项目
│ ├── src/
│ ├── pom.xml
│ └── Dockerfile <-- 后端 Dockerfile
│
├── frontend/ # Vue 项目
│ ├── src/
│ ├── package.json
│ ├── Dockerfile <-- 前端 Dockerfile
│ └── nginx.conf <-- 前端 Nginx 配置
│
└── docker-compose.yml <-- Docker 编排文件
📄 配置文件
-
后端 Dockerfile (
Dockerfilebackend/Dockerfile
):FROM openjdk:11-jre-slim # 将 target 目录下的 jar 包复制到容器中,并重命名 COPY target/ruoyi-system.jar /app.jar # 容器启动时运行 jar 包 CMD ["java", "-jar", "/app.jar"]
-
前端 Nginx 配置 (frontend/nginx.conf) (关键!):
这个 Nginx 配置是给容器内部使用的,它将 API 请求转发给名为 backend 的服务。
Nginxserver { listen 80; location / { root /usr/share/nginx/html; try_files $uri $uri/ /index.html; } location /api { # 这里的 "backend" 是 docker-compose.yml 中定义的服务名 # Docker 的内部 DNS 会将其解析到后端容器的 IP proxy_pass http://backend:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }
-
前端 Dockerfile (frontend/Dockerfile):
这个 Dockerfile 会先构建 Vue 应用,然后将构建结果和 Nginx 配置一起打包。
Dockerfile# --- 构建阶段 --- FROM node:lts-alpine as build-stage WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build # --- 生产阶段 --- FROM nginx:stable-alpine # 从构建阶段复制打包好的静态文件 COPY --from=build-stage /app/dist /usr/share/nginx/html # 复制自定义的 Nginx 配置文件 COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]
-
Docker Compose (
YAMLdocker-compose.yml
):version: '3.8' services: backend: build: ./backend # 指定后端项目的构建上下文 ports: - "8080:8080" # 将容器的8080端口映射到主机的8080端口 restart: always frontend: build: ./frontend # 指定前端项目的构建上下文 ports: - "80:80" # 将容器的80端口(Nginx)映射到主机的80端口 restart: always depends_on: # 确保后端服务先于前端启动 - backend
🚀 启动
在项目根目录(my-project/
)下,执行一个命令即可启动所有服务:
docker-compose up -d --build
up
: 创建并启动容器。-d
: 后台运行。--build
: 在启动前构建镜像。
现在,访问你服务器的公网 IP 或域名,就可以看到由 Docker 运行的前端应用,并且它能成功调用同样由 Docker 运行的后端服务。