不能直接用 docker build 构建前端镜像,因未分离构建与部署阶段导致缓存失效、ci 耗时高;应采用多阶段构建,用 node 镜像构建、nginx 镜像运行,并严格分层缓存、精准复制、子域名隔离、nginx fallback 配置及人工审批关键环节。

为什么不能直接用 docker build 构建前端镜像?
很多团队一上来就写 Dockerfile,用 npm install && npm run build 打包再 COPY 到 Nginx 镜像里,结果发现每次改一行 CSS 都要重跑整个构建——依赖安装 + TypeScript 编译 + 依赖树解析全来一遍,CI 耗时暴涨。根本问题在于:前端构建不是“一次编译、永久运行”,而是高频、增量、依赖缓存敏感的过程。
真正该隔离的不是最终静态文件,而是构建环境本身。所以核心思路是:把构建和部署拆成两个镜像阶段,用多阶段构建(multi-stage)+ 构建缓存分层控制,而不是把 node_modules 和 dist 硬塞进一个镜像。
-
FROM node:18-alpine仅用于构建,安装依赖后立即rm -rf node_modules/.pnpm(若用 pnpm)或清理未锁定的临时目录,避免污染后续层 - 构建产物必须用
COPY --from=builder /app/dist /usr/share/nginx/html显式复制,禁止COPY . /app这类宽泛操作,否则 Docker 会因 .git/.env 等文件变动导致缓存失效 - 生产镜像基础必须用
nginx:alpine-slim或nginx:1.25-alpine,别用nginx:latest—— 后者可能突然升级引入sendfile行为变更,导致某些 chunk 加载失败
如何让不同分支/PR 的预览 URL 自动隔离不冲突?
关键不在 Nginx 配置,而在容器编排层的路由分发逻辑。如果所有预览都走 preview.example.com + path(如 /pr-42),前端路由(React Router / Vue Router)的 base 配置、publicPath、以及 nginx.conf 中的 location 嵌套层级很容易错位,404 大量出现。
更稳的做法是:每个预览实例绑定唯一子域名,由反向代理(如 Traefik / Nginx Ingress)按标签自动路由。Docker Compose 启动时通过 labels 注入分支名,K8s 则用 ingress.spec.rules.host 动态生成。
- 镜像构建时传参:
docker build --build-arg BRANCH_NAME=feat/login --tag my-fe:feat-login . - 容器启动时加 label:
labels: ["traefik.http.routers.preview-<code>BRANCH_NAME.rule=Host(`BRANCH_NAME.preview.example.com`)"] - 前端构建需注入
BASE_URL:在package.json的buildscript 中加--base /(注意不是--base ./),否则打包后资源路径会相对当前路径,子域名下加载 404
docker run 启动后页面空白,但控制台无报错?
这大概率是 Nginx 默认配置没处理前端路由回退(fallback)。比如访问 feat-login.preview.example.com/user/profile,Nginx 找不到对应物理文件,直接返回 404,而没把请求交给 index.html 让前端 Router 处理。
解决方式不是改 index.html,而是改 Nginx 的 location 块。不要用网上流传的“try_files $uri $uri/ /index.html;”这种粗暴写法——它会在匹配到 /api 时也 fallback,导致 API 请求被当成前端路由。
- 正确做法:单独配 API 代理规则,再对静态资源做精准 fallback
location / {<br> try_files $uri $uri/ /index.html;<br>}<br>location ^~ /api/ {<br> proxy_pass http://backend;<br>} - 务必检查
nginx.conf中root指向是否与镜像内实际路径一致(如/usr/share/nginx/html),常见错误是写成/app/dist但镜像里压根没这个路径 - 若用 Create React App,确保
homepage字段设为"https://<code>BRANCH_NAME.preview.example.com",否则生成的manifest.json和asset-manifest.json里的路径仍指向根域
自动化部署链路中哪些环节必须人工确认?
CI 流水线可以全自动推镜像、启容器、更新 DNS,但有两处绝不能跳过人工干预:一是主干分支(main/master)的上线,二是涉及 CDN 缓存刷新的发布(比如用了 Cloudflare Page Rules 或阿里云 CDN 的强制刷新)。
-
main分支部署必须触发 Slack / 飞书审批机器人,且审批人需核对本次合并的 PR 列表中是否有高风险改动(如路由结构调整、全局样式覆盖、第三方 SDK 升级) - CDN 缓存策略若设为 “max-age=31536000”,那么即使你重新部署了新镜像,用户浏览器仍可能加载旧版 JS —— 此时光重启容器没用,必须调用 CDN 的 Purge API,且 purge 路径要精确到
/static/js/main.*.js这类带 hash 的文件,不能只 purge/ - 镜像 tag 命名必须含 Git SHA(如
my-fe:sha-a1b2c3d),禁止只用latest或main。否则 rollback 时无法精准定位上一版镜像,排查问题成本陡增
最易被忽略的是:构建镜像时没有校验 package-lock.json 是否被修改却未提交。CI 中应加一步 git status --porcelain | grep 'package-lock.json' && exit 1 || true,否则本地锁文件和 CI 环境不一致,会导致依赖版本漂移,某天突然白屏。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











