前端项目怎样用 Docker 和 Nginx 部署?缓存头应该怎么配?

先理解原理和示例,再练习完整口述与追问。

多阶段构建:第一阶段用 Node 镜像安装依赖并构建,第二阶段只把产物复制进 Nginx 镜像,镜像小、无源码与 node_modules。Nginx 配置:带哈希的静态资源长期缓存加 immutable,index.html 使用 no-cache 每次协商,history 路由用 try_files 回退,开启 gzip 或 Brotli,按需反向代理 API。

Dockerfile 中先复制 package.json 和锁文件再安装依赖,利用层缓存:源码变化不会重新安装依赖。.dockerignore 排除 node_modules、.git 和本地环境文件,防止上下文过大和泄露。前端构建变量在构建时写入产物,同一镜像要部署到多个环境,可以在容器启动时生成 config.js 或由 Nginx 注入运行时配置。

文件名带内容哈希时,内容变化文件名就变,可安全缓存一年;index.html 引用这些文件名,必须每次验证,否则用户会拿到旧的入口。no-cache 表示可以缓存但使用前必须向服务器验证,no-store 才是完全不缓存。

dockerfile
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:1.27-alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html
nginx
server {
  listen 80;
  root /usr/share/nginx/html;
  gzip on;
  gzip_types text/css application/javascript application/json image/svg+xml;
  location /assets/ { add_header Cache-Control "public, max-age=31536000, immutable"; }
  location = /index.html { add_header Cache-Control "no-cache"; }
  location /api/ { proxy_pass http://backend:3000/; }
  location / { try_files $uri $uri/ /index.html; }
}

在镜像中保留 .env 或源码映射会泄露信息;Nginx 中 add_header 写在子 location 后,父级的 add_header 不会继承。滚动发布期间,旧页面可能请求已被删除的旧 chunk,需要保留上一版本资源一段时间。

我用多阶段 Dockerfile:第一阶段 Node 镜像 npm ci 后构建,第二阶段只把 dist 复制进 Nginx,镜像小、不带源码;先复制锁文件再安装依赖,利用层缓存。Nginx 上带哈希的资源设置一年缓存加 immutable,index.html 设 no-cache 每次协商,history 路由用 try_files 回退 index.html,开启压缩,API 通过反向代理。发布时保留上一版资源,避免用户打开的旧页面加载 chunk 失败。