前言
Vue、React等现代前端工程在本地跑通后,总要面对「怎么上线、在哪跑、谁提供静态资源」这些问题。把打包好的 dist 丢到服务器只是第一步,选对部署方式,能少踩环境不一致、路由 404、重复构建慢等深坑。
目前常见的做法大致分为两种:一、在服务器上直接装 Nginx,用主机上的 Nginx 提供静态文件(直装部署);二、用 Docker 跑 Nginx 或把「构建 + Nginx」打成一个镜像(容器化部署)。
前者配置简单、和现有 Nginx 环境兼容好,适合单机、内网或快速验证;后者环境一致、易做 CI/CD 和版本回滚,适合多环境、团队协作或离线交付场景。我们团队就是先上了直装,后期devops引入流水线时才迁到容器化的。
如果你正在给 Vue 前端选部署方案,或打算从直装迁到 Docker,希望这篇文章能手把手带你解决问题。
一、直装部署
直装部署顾名思义就是指在宿主机上直接安装 Nginx,将前端构建产物(如 dist)放到指定目录,由主机上的 Nginx 提供静态资源。适合单机、快速验证或已有 Nginx 环境的场景。
1.1 流程概览与安装
流程
flowchart TD
Start([开始部署]) --> Install[apt install nginx]
Install --> Config[配置 site 与 nginx.conf]
Config --> Test[nginx -t 校验]
Test --> Host[配置本机 /etc/hosts]
Host --> Upload[上传 dist 到站点目录]
Upload --> Reload[systemctl reload nginx]
Reload --> End([访问站点域名])
安装与基础配置
在 Ubuntu 上安装 Nginx 并确认服务正常:
apt update
apt install nginx -y
systemctl status nginx
查找主配置文件位置(一般为 /etc/nginx/nginx.conf):
find . -name "nginx.conf"
# 编辑主配置(如需)
vi /etc/nginx/nginx.conf
1.2 站点配置与访问验证
站点配置(如 ababa.conf)
在 conf.d 下新增站点配置:
server {
listen 80;
server_name ababa.com;
location / {
root /home/website/vue-project;
index index.html;
}
}
要点:
root指向前端构建产物所在的目录(后续把dist内容放到该目录)。若为 SPA,需保证 Nginx 对前端路由做 fallback(见「SPA 路由回退」)。
校验并重载:
nginx -t
systemctl reload nginx
本机 hosts 与访问
在开发/测试机上把域名指到服务器 IP:
sudo vi /etc/hosts
# 新增一行(IP 改为你的服务器地址)
# 10.211.55.4 ababa.com
将前端项目的 dist 上传到 /home/website/vue-project,访问 http://ababa.com 即可。
SPA 路由回退(可选)
Vue Router 使用 history 模式时,需让 Nginx 把未匹配到文件的请求都返回 index.html,配置如下:
location / {
root /home/website/vue-project;
index index.html;
try_files $uri $uri/ /index.html;
}
1.3 简易实现一个自动化部署:Husky + rsync
在直装方案下,若希望每次 git commit 时自动构建并同步到服务器,可用 Husky 触发部署脚本(内部执行 build + rsync)。
流程
flowchart TD
Dev["开发者 git commit"] --> Husky["Husky pre-commit 钩子"]
Husky --> Build["执行 deploy.sh: npm run build"]
Build --> Rsync["rsync 同步 dist 到服务器"]
Rsync --> Dir["服务器目录<br/>/home/website/vue-project"]
Dir --> Nginx["Nginx 提供静态文件"]
Nginx --> End(["用户访问"])
实现步骤
安装 rsync(Mac)与 Husky:
brew install rsync
pnpm i husky -D
npx husky install
部署脚本 .husky/deploy.sh:
#!/bin/sh
# 构建
npm run build
# 上传部署(按需改端口、用户、路径)
rsync -avz --delete -e "ssh -p 22" ./dist/ root@ubuntu:/home/website/vue-project
pre-commit 钩子(.husky/pre-commit):
#!/usr/bin/env sh
sh $(dirname -- "$0")/deploy.sh
赋予执行权限:
chmod +x .husky/deploy.sh
之后每次 git commit 会先执行构建再 rsync 到服务器,适合小团队快速更新测试/演示环境;生产环境仍建议走 CI/CD 与审批流程。
二、容器化部署
容器化部署是将 Nginx 与静态资源(或构建过程)封装在 Docker 镜像/容器中,通过镜像与编排实现环境一致、一键启停和版本化的部署。
2.1 总体流程
flowchart LR
subgraph 构建
Dockerfile[Dockerfile] --> Build[docker build]
Build --> Image[镜像]
end
subgraph 运行
Image --> Run[docker run / compose up]
Run --> Container[Nginx 容器]
Container --> Port[端口映射 宿主机:容器]
end
容器内 Nginx 与宿主机上的 Nginx 互不干扰,可同时存在。
使用 docker-compose 挂载已有目录
若宿主机上已有构建好的 dist(如放在 /home/website/vue-project),可用 compose 只起一个 Nginx 容器并挂载该目录:
services:
nginx:
container_name: nginx
restart: always
image: nginx
environment:
- TZ=Asia/Shanghai
ports:
- "8082:80"
volumes:
- /home/website/vue-project:/usr/share/nginx/html
启动与查看日志:
docker compose up -d
docker logs -f nginx
访问 http://宿主机IP:8082 即可。宿主机 Nginx 停掉时,容器内 Nginx 仍可独立对外提供页面。
挂载宿主机 Nginx 配置
若希望容器使用与直装一致的 Nginx 配置(多站点、自定义 server 等),可挂载配置与站点目录:
services:
nginx:
container_name: nginx
restart: always
image: nginx
environment:
- TZ=Asia/Shanghai
ports:
- "8082:80"
volumes:
- /etc/nginx/conf.d:/etc/nginx/conf.d/
- /etc/nginx/nginx.conf:/etc/nginx/nginx.conf
- /home/website/:/home/website
使用自定义 compose 文件时:
docker compose -f docker-compose.nginx.yml up -d
便于从直装平滑迁移到容器。
2.2 Dockerfile 多阶段构建
在容器化方案中,除了「宿主机先构建再挂载」,还可以在镜像内完成构建,得到「构建 + Nginx」一体的镜像。
构建与运行流程
flowchart TD
subgraph 构建阶段
S1[Stage1: node 镜像] --> S1_1[COPY package*.json]
S1_1 --> S1_2[pnpm install]
S1_2 --> S1_3[COPY 源码]
S1_3 --> S1_4[npm run build]
S1_4 --> S2[Stage2: nginx 镜像]
S2 --> S2_1[COPY dist 到 /usr/share/nginx/html]
S2_1 --> S2_2[EXPOSE 80]
end
S2_2 --> Image[最终镜像]
Image --> Run[docker run -p 18080:80]
多阶段构建好处:第一阶段用 Node 安装依赖并构建,第二阶段只保留 dist + Nginx,最终镜像体积小、无源码与 node_modules。
单 Dockerfile 示例
# build stage(使用国内镜像,避免 Docker Hub 超时)
FROM docker.1ms.run/library/node:20-alpine AS build-stage
WORKDIR /app
COPY package*.json ./
COPY pnpm*.yaml ./
RUN npm install -g pnpm --registry=https://registry.npmmirror.com && \
npm config set registry=https://registry.npmmirror.com && \
pnpm install
COPY . .
ENV NODE_OPTIONS="--experimental-require-module"
RUN npm run build
# production stage
FROM docker.1ms.run/library/nginx:stable-alpine AS production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
构建与运行:
docker build -t project:1.0 .
docker run -itd --name test -p 18088:80 fproject:1.0
访问 http://localhost:18088/ 即可看到前端页面。
2.3 构建优化与镜像迁移
构建优化:依赖层与产物层分离
当依赖体积较大时,可把「安装依赖」和「拷贝源码并构建」拆开,避免每次改代码都重新 pnpm install:
flowchart LR
DockerfileDev[Dockerfile-dev] --> DevImage[dev:1.0 镜像]
DevImage --> DockerfileProd[Dockerfile-prod]
DockerfileProd --> ProdImage[project:2.0]
Dockerfile-dev(只装依赖,可反复复用):
FROM docker.1ms.run/library/node:20-alpine AS build-stage
WORKDIR /app
COPY package*.json ./
COPY pnpm*.yaml ./
RUN npm install -g pnpm --registry=https://registry.npmmirror.com && \
npm config set registry=https://registry.npmmirror.com && \
pnpm install
docker build -f Dockerfile-dev -t dev:1.0 .
Dockerfile-prod(基于 dev 镜像,只做构建与 Nginx):
FROM dev:1.0 AS build-stage
WORKDIR /app
COPY . .
ENV NODE_OPTIONS="--experimental-require-module"
RUN npm run build
FROM docker.1ms.run/library/nginx:stable-alpine AS production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
docker build -f Dockerfile-prod -t project:2.0 .
docker run -itd --name test1 -p 18088:80 project:2.0
依赖变更时才重打 dev:1.0,日常改代码只需重打 project:2.0,加快迭代。
镜像导出与恢复
有时在税局封闭开发,只能使用内网,那么在内网或无外网环境中,也可通过「导出 → 拷贝 → 导入」迁移镜像:
sequenceDiagram
participant 构建机 as 构建机
participant 文件 as tar 文件
participant 目标机 as 目标服务器
构建机->>文件: docker save -o dev.1.0.tar dev:1.0
构建机->>目标机: 上传 dev.1.0.tar(scp/ U 盘等)
目标机->>目标机: docker load -i dev.1.0.tar
# 导出
docker save -o dev.1.0.tar dev:1.0
# 在目标机器导入
docker load -i dev.1.0.tar
只适用于离线环境或需要固定版本交付的场景。
三、总结与对比
3.1 两种方式对比
| 维度 | 直装部署 | 容器化部署 |
|---|---|---|
| 环境一致性 | 依赖主机系统,易受系统环境影响 | 镜像内环境一致,可跨机器复现 |
| 运维成本 | 需单独安装 Nginx、配置站点、管理进程 | 一条命令起停,配置可挂载或打进镜像 |
| 构建与发布 | 本地/CI 构建后上传 dist | 可在镜像内构建,或先构建再挂载 |
| 自动化 | 易与 Husky + rsync 结合做 commit 部署 | 易与 CI/CD 流水线结合,镜像 tag 即版本 |
| 适用场景 | 单机、快速验证、已有 Nginx 主机 | 多环境、CI/CD、需版本化、离线交付 |
3.2 流程对比示意
flowchart LR
subgraph 直装部署
A1[源码构建] --> A2[上传 dist]
A2 --> A3[主机 Nginx]
A3 --> A4[浏览器]
end
subgraph 容器化部署
B1[Dockerfile / 构建] --> B2[镜像]
B2 --> B3[容器 Nginx]
B3 --> B4[浏览器]
end
3.3 小结
- 直装部署:适合单机、已有 Nginx、快速验证的场景;配置好
server、root、try_files即可。 - 容器化部署:适合要求多环境一致、CI/CD、版本化部署等场景;可用 compose 挂载目录或配置,也可用 Dockerfile 打出「构建 + Nginx」一体的镜像
按「直装 → 容器化(compose 挂载 → Dockerfile 多阶段 → 优化与迁移)」这条线动手实践,基本就可以覆盖从本机 Nginx 到容器化与自动化的大部分场景啦,掘友们快来试试吧!