给后端程序员的科普 —— Nginx 在前端部署中究竟扮演什么角色?

给后端程序员的科普 —— Nginx 在前端部署中究竟扮演什么角色?
一、从后端开发者的直觉困惑说起Spring Boot 项目的部署流程非常简单mvn clean package-DskipTests# 产出 app.jardockerbuild-tmy-app.# 打包成镜像dockerrun my-app# 跑起来浏览器就能访问了于是自然产生一个疑问Vue/React 项目不能这样吗npm run build也有产物dist/文件夹为什么不能直接docker runnpmrun build# 产出 dist/ 文件夹dockerrun my-app# ❌ 容器直接死掉什么也访问不了答案藏在产物的本质差异里。二、关键差异产物里有没有 HTTP 服务器后端 jar 包前端 dist 文件夹产物是什么app.jar可执行文件index.html.js.css纯数据文件包含 HTTP 服务器吗✅ 内嵌 Tomcat❌ 不包含任何服务器能监听端口吗✅ Tomcat 自动监听 8080❌ 没有进程监听能响应 HTTP 请求吗✅ Controller 在等请求❌ 没有人收请求用后端术语类比dist/文件夹本质上等于src/main/resources/static/下的静态文件——它们是数据不是服务。Spring Boot 里放个index.html能访问不是因为 HTML 自己能跑而是因为Tomcat在对外暴露。把 Tomcat 去掉那个 HTML 文件就是硬盘上一段字节TCP 连接都建不起来。Vuedist/文件夹就是去掉了 Tomcat 的那些静态文件。三、Docker 的运行本质很多开发者对 Docker 有一个常见误解以为它是一个虚拟机里面放了文件就能跑。Docker 启动的是进程进程死了容器就死。# 这样写没有意义 FROM alpine COPY ./dist /app # 光拷贝文件没有任何进程要跑docker run之后容器里一个进程都没有Docker 发现没有前台进程 → 容器立刻退出。就算强行不让它退出80 端口也是空的——没有程序在那里调用socket.listen(80)TCP 握手都完成不了。核心矛盾curl http://localhost:80/这个请求得有人在 80 端口执行recv()才行。那个人在哪四、Nginx 就是那个服务员Nginx 是一个持续运行的 C 程序做了三件事1. 启动后不退出进程活着 → 容器不死 2. 调用 socket.listen(80) 监听 80 端口有人接电话了 3. 收到 HTTP 请求 → 读硬盘上的文件 → 返回给浏览器放进 DockerfileFROM nginx:latest # 基础镜像自带 nginx 程序 COPY ./dist /usr/share/nginx/dist # 前端文件放进 nginx 的静态目录执行docker run后nginx 进程跑起来 → 监听 80 端口 → 浏览器访问http://localhost/index.html→ nginx 读取/usr/share/nginx/dist/index.html→ 返回文件内容。请求链路浏览器 容器 │ │ │ GET /index.html │ │ ──────────────────────────────→ │ nginx 进程 (在 80 端口 listen) │ │ │ │ 1. accept() 建立连接 │ │ 2. 解析 HTTP拿到路径 /index.html │ │ 3. open() 读硬盘文件 │ │ 4. send() 把字节塞进 HTTP 200 响应 │ │ │ ←── HTTP 200 html... │ │ │五、为什么不用 webpack-dev-servernpm run dev启动的开发服务器webpack-dev-server在本地调试时确实在localhost:8080提供服务。但它绝对不能上生产webpack-dev-servernginx设计目的本地开发调试生产环境热更新有每次改动自动刷新不需要性能模型Node.js 单线程一个请求走 webpack 编译管线C 语言 epoll直接从 OS page cache 读文件内存300MB~5MB并发能力几百数万webpack-dev-server 上生产 用自行车跑高速。不是走不通是性能和安全都撑不住。六、Nginx 的另一项关键能力SPA 路由回退Vue Router 在history模式下用户访问/contract/detail/123但dist/文件夹里根本没有contract/detail/123.html这个文件。为什么不是 404因为路由解析在浏览器端完成。服务器面对任何路径只需要返回index.html然后 Vue Router 在浏览器里看到路径再决定渲染哪个组件。这需要 nginx 配一条关键规则location / { try_files $uri $uri/ rewrites; # 先尝试找实际文件 } location rewrites { rewrite ^(.)$ /index.html last; # 找不到就统一返回 index.html }翻译“磁盘上没这个文件那就返回 index.html让前端路由自己看着办。”没有这条配置用户刷新/contract/detail/123页面nginx 老老实实去找文件找不到就返回 404——这就是前端开发中经典的SPA 刷新 404 问题。七、前后端对比一张图总结后端 Java 应用 ┌────────────────────────────┐ │ app.jar │ │ ┌──────────────────────┐ │ │ │ Tomcat (服务员) │ │ ← jar 包自带的 │ │ 监听 8080 端口 │ │ │ │ 执行 Controller │ │ │ └──────────────────────┘ │ │ ┌──────────────────────┐ │ │ │ static/ 静态资源 │ │ ← 没有 Tomcat这些也是死文件 │ └──────────────────────┘ │ └────────────────────────────┘ 前端 Vue 应用 ┌────────────────────────────┐ │ Docker 镜像 │ │ ┌──────────────────────┐ │ │ │ nginx (服务员) │ │ ← 得自己配一个 │ │ 监听 80 端口 │ │ │ └──────────────────────┘ │ │ ┌──────────────────────┐ │ │ │ dist/ 前端文件 │ │ ← 只有文件没有服务器 │ └──────────────────────┘ │ └────────────────────────────┘核心结论后端 jar 包自带服务员内嵌 Tomcat前端 build 出来只有菜单没有服务员所以必须配一个 nginx 来当服务员。两者的部署流水线逻辑完全一致区别只在于集装箱里装的是谁。