📝 前言
这是本系列的终篇,也是很多人在第一次部署个人全栈项目时,踩坑最多、最容易令人绝望的一个环节 ------ 前端打包部署。
在本地开发 Vue/Vite 或 React 时,我们在 request.js 里写一个 http://localhost:8080/api 就觉得高枕无忧了。但一把构建丢到服务器上,才猛然发现接口死活都不通(抛出 ERR_CONNECTION_REFUSED 或是 CORS 跨域错误)。而且在国内服务器经常遇到 npm install 卡死抛错 ETIMEDOUT。
这篇博客,我就带大家一一跨越这些"致命天坑",使用最佳实践:前端多阶段构建 + Nginx 静态驱动 + Nginx 反向代理解决跨域连通。
🏔️ 第一坑:国内构建 npm install 超时天坑
如果你的项目是在阿X云、腾X云等国内机房服务器里构建的,不要想着直接 npm i,一定要在 Dockerfile 里配置国内强力镜像及代理。
针对由于网络不好引发的依赖丢失(比如经常发现 vite 构建丢了依赖),可以在 frontend/vue/Dockerfile 这么写:
dockerfile
# ---- 阶段 1:构建阶段 ----
FROM docker.m.daocloud.io/node:18-alpine AS build
WORKDIR /app
# 先单独拷贝打包信息,利用Docker缓存机制
COPY package.json package-lock.json* ./
# 【关键配置】设置 npm 环境变量使用国内源进行极速下载
ENV NPM_CONFIG_REGISTRY=https://registry.npmmirror.com/
# 如果出现 npm gyp 或编译依赖问题,安装 python 与 make (可选,看项目库)
# RUN apk add --no-cache python3 make g++
# 强制安装避免警告造成的意外失败
RUN npm install --production=false --omit=dev=false --loglevel verbose
# 规避偶发的 vite 或 rollup 二进制文件缺失
RUN npm install -g vite@4.5.14
# 拷贝全部源码
COPY . .
# 通过传参覆盖内置环境变量,使得所有 axios api 配置变成空前缀 (下面重点讲)
ARG VITE_API_BASE_URL=""
ENV VITE_API_BASE_URL=${VITE_API_BASE_URL}
# 使用 npx 显式打包,避免软链丢失引起的 command not found 错误
RUN npx vite build
# ---- 阶段 2:运行阶段(Nginx) ----
FROM docker.m.daocloud.io/nginx:alpine
# 将产物转移进 Nginx 默认目录
COPY --from=build /app/dist /usr/share/nginx/html
# 覆盖我们的专属 Nginx 配置
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
⚠️ Linux 的大小写敏感天坑
这里经常会面临一个很容易忽视的报错:
本地 Windows 下,你从 @/components/Layout/ 导入了一个文件,但在目录里文件夹叫 @/components/layout/!这在本地是没问题的,但因为基于 alpine Linux 的容器是严格区分大小写的! 打包直接就会抛出 Failed to resolve import 的死循环中,一定去你的源代码里检查大小写匹配!
🔄 第二招:Nginx 配置与动态反向代理
为了不让我们的浏览端(用户客户端)直接访问 SpringBoot (跨域灾难),我们直接用前端的同一个服务 Nginx 拦截对 /api 开头的请求,然后转发到刚才的中篇的容器里。
在 frontend/vue/nginx.conf 编写:
nginx
server {
listen 80;
server_name localhost;
# 这里处理单页应用的路由回退,如果使用 Vue Router 的 history 模式,必写这部分
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
# Nginx 拦截 /api 动态反向代理到局域网内的后端容器!
location /api/ {
# 注意此处的 project001-backend 是 Docker-compose 的服务名!
proxy_pass http://project001-backend: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;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 代理 /uploads 处理静态文件的回放上传
location /uploads/ {
proxy_pass http://project001-backend:8080/uploads/;
}
}
💣 最恐怖神坑:写死的默认 localhost:8080 导致拒收连接
如果你部署完发现页面出来了,但是点击登录,控制台报错:ERR_CONNECTION_REFUSED http://localhost:8080/api/auth/login。
不要去服务器上找 8080 这个端口!由于前端跑在客户浏览器里(也就是你的个人电脑游览器而不是云主机),它拼装并去请求你"个人电脑"的 8080 端口,那它怎么可能连的上远在云服务器上的真实后端呢?
最佳解决方案:
- 检查前端源码里的
src/api/request.js或者 Vite 配置(.env.production); - 绝对不能使用
http://localhost:8080做为保底参数; - 将前端请求配置改为:
baseURL: import.meta.env.VITE_API_BASE_URL || ''(也就是相对路径)。 - 结合 Docker-compose 参数:
yaml
project001-frontend:
build:
context: ./frontend/vue
args:
# 在这里把前缀设为空,编译出来的 JS 因为前缀空,默认会打往本域的 /api/...
# 然后本域的 80 Nginx 就会通过反向代理吃掉它扔给后端!
VITE_API_BASE_URL: ""
container_name: project001-frontend
ports:
- "80:80"
最后,遇到缓存问题:即便部署对了,务必在 Chrome 浏览器点右键:强制清除缓存并硬性重新加载浏览器。前端旧的打包 JS 带毒太严重。
🎉 最后:One-Command Deploy
当你排清了上述坑,此时在你的服务器项目根目录只需要轻轻敲下一句:
bash
docker rm -f $(docker ps -aq) && docker compose up -d --build
然后甩出你的这台虚拟机的公网 IP ,发给小伙伴:
"哎,朋友,来看看我刚上线的全栈系统爽不爽?"
一切就这么完成了!完结撒花。
欢迎点赞收藏评论,这是我从 0 到 1 上线真实项目的泣血总结!希望这套体系能直接助你拿下 Offer。