真实工作里,很多前端项目依然不是一键流水线发布
在不少团队中,前端项目虽然已经使用了 Vue、React、Vite、TypeScript 这一类现代化技术栈,但部署方式未必已经完全自动化。尤其是在中小团队、老项目迭代项目,或者需要谨慎上线的业务场景里,仍然经常能看到一种非常典型的部署方式:人工拉代码、人工打包、人工替换 dist、人工重启容器。
这种方式谈不上优雅,但胜在直接、可控、容易理解。对于很多刚接触线上部署的前端同学来说,先理解这一套“古法部署”流程,反而有助于建立对项目发布链路的整体认知。
本文就以一个通用的 Vue3 + TypeScript + Vite 前端项目为例,梳理一遍真实工作中常见的手动部署流程,并补充两种常见的静态资源替换方案。
一、古法部署到底在做什么
如果用一句话概括,所谓前端古法部署,本质上就是下面这几步:
- 从远程仓库获取最新代码。
- 切换到主分支,例如
main或master。 - 拉取最新代码并安装依赖。
- 执行 Vite 打包,产出新的
dist目录。 - 将新的
dist替换到服务器实际提供静态资源的位置。 - 重启对应的 Docker 容器。
- 打开线上页面,确认更新已经生效。
之所以要把流程拆得这么细,是因为线上部署不是单纯执行一条命令,而是一个完整的“代码更新 -> 产物构建 -> 资源替换 -> 服务重启 -> 发布验证”的过程。
二、第一步:获取远程最新代码
在开始部署之前,首先要确认本地代码是最新的。通常我会先拉取远程更新,再切换主分支。
git fetch origin
git switch main
git pull如果项目主分支不是 main,而是 master,那么把上面的命令改成:
git fetch origin
git switch master
git pull这里有两个容易忽略的点:
git fetch origin只是获取远程变化,不会直接改动当前工作区。git pull应该尽量在主分支上执行,避免把其他功能分支的代码误打到线上。
如果你的工作习惯更谨慎,也可以先看一下当前分支状态:
git branch
git status只有确认当前目录、当前分支和代码状态都没有问题,才建议继续往下做。
三、第二步:安装依赖并执行 Vite 打包
当代码更新完成后,下一步就是安装依赖并重新构建项目。
对于一个 Vite 项目来说,常见的打包命令一般如下:
pnpm install
pnpm run build如果项目使用的是其他包管理器,也可以对应替换为:
npm install
npm run buildyarn install
yarn run buildbun install
bun run build在真实工作中,应该优先使用项目原本采用的包管理器,不要混用。例如项目里如果已经存在 pnpm-lock.yaml,那就优先使用 pnpm;如果是 package-lock.json,就优先使用 npm。
对于 Vue3 + TypeScript + Vite 这类项目,打包完成后通常会生成一个新的 dist 目录。这个目录就是最终要部署到线上静态资源服务中的内容。
四、打包前后建议检查什么
很多部署问题并不是出在“不会打包”,而是出在“打包之前和打包之后没确认”。
建议至少检查下面这些内容:
- 当前环境变量是否正确,例如
.env.production是否符合线上配置。 - 本次上线的功能是否已经合并到主分支。
- 构建过程是否报错,是否出现 TypeScript 类型错误、依赖缺失或者内存不足。
- 打包输出的
dist是否正常生成,目录内容是否完整。
如果构建已经失败,就不要继续进行后面的资源替换,否则很容易出现页面白屏、资源 404 或接口配置异常。
五、第三步:替换服务器中的 dist
手动部署里最核心的一步,就是把刚刚生成的 dist 替换到服务器实际运行的位置。
这一块常见有两种场景:
1. 有宿主机挂载目录
这是最省事的一种情况。也就是 Docker 容器中的静态资源目录,实际上挂载到了宿主机上的某个目录。此时只需要把新打包出来的 dist 替换宿主机挂载目录中的旧 dist 即可。
可以把流程理解成这样:
本地重新打包
↓
得到新的 dist
↓
上传或拷贝到服务器宿主机挂载目录
↓
替换旧 dist
↓
容器内静态资源同步更新这类场景的优势是简单直接,因为容器本身读取的就是挂载目录里的文件。只要宿主机上的 dist 替换完成,容器侧通常就能拿到新的静态资源。
实际操作时,建议注意两点:
- 替换前先备份旧的
dist,防止上线后需要快速回滚。 - 替换时尽量确保文件完整覆盖,避免旧文件残留导致资源版本不一致。
2. 没有宿主机挂载目录
如果容器内部的静态资源目录没有挂载出来,那么就不能只改宿主机目录,而是需要先把新的 dist 传到宿主机,再通过 docker exec 进入容器内部执行替换。
可以把这个流程理解成:
本地打包生成 dist
↓
上传 dist 到宿主机临时目录
↓
通过 docker exec 进入容器
↓
删除容器内旧 dist
↓
复制新的 dist 到容器指定目录一个比较常见的思路如下:
docker exec -it your-nginx-container sh进入容器后,再执行类似的替换操作:
cd /usr/share/nginx/html
rm -rf dist
cp -r /tmp/dist ./dist这里的路径只是示例,真实环境里需要根据容器内静态资源的实际目录来调整。
如果不想进入交互式终端,也可以直接执行单条命令式替换,不过这类命令更容易写错,所以在手动部署场景里,很多团队仍然会选择先进入容器再处理。
六、第四步:重启 Docker 容器
当新的 dist 已经替换完成后,下一步通常就是重启容器,让服务以最新状态运行。
docker restart your-frontend-container这里的 your-frontend-container 是示例容器名,实际执行时请替换成真实的容器名称。
为什么很多静态资源项目替换完文件后仍然会选择重启容器?
- 为了确保 Nginx 或静态资源服务读取到最新文件。
- 为了减少缓存、进程状态或挂载同步带来的不确定性。
- 为了让部署流程更统一,降低“这次到底要不要重启”的沟通成本。
虽然某些场景下不重启也能生效,但在真实工作里,统一、明确、可复现 的发布动作往往比“理论上可省略一步”更重要。
七、第五步:上线后进行结果验证
部署完成并不代表发布结束,最后一步一定是人工检查。
最基本的验证方式包括:
- 打开线上站点首页,确认页面是否能正常加载。
- 检查本次发布的功能是否已经展示出来。
- 打开浏览器控制台,确认没有明显的静态资源 404 或脚本报错。
- 进行一次强制刷新,观察缓存是否影响了资源更新。
如果项目接入了监控平台、日志平台或者前端埋点系统,也可以顺手看一下错误率是否在发布后异常上升。
八、一个更适合团队记录的通用部署清单
如果你想把这套流程沉淀成团队文档,可以直接整理成下面这种操作清单:
git fetch origingit switch main或git switch mastergit pull- 使用项目对应的包管理器安装依赖
- 执行
Vite项目打包命令,生成新的dist - 按照部署环境选择替换方式
- 有挂载目录:直接替换宿主机挂载目录中的
dist - 无挂载目录:先传输到宿主机,再通过
docker exec进入容器替换dist - 执行
docker restart 容器名 - 打开线上页面,确认发布结果
九、这种部署方式的优点与问题
古法部署虽然“土”,但它确实有自己的适用场景。
优点主要有:
- 容易理解:对前端同学来说,上手成本低,不需要先学习完整的 DevOps 体系。
- 可控性强:每一步都由人明确执行,适合对上线节奏要求谨慎的项目。
- 排查直观:如果出问题,能快速定位是在拉代码、打包、替换还是容器重启这一步出现了异常。
但问题也很明显:
- 高度依赖人工:容易漏步骤、打错命令、切错分支。
- 重复劳动多:每次上线都要重复执行同样的操作。
- 回滚不够优雅:没有自动化版本管理时,回退常常依赖手工备份。
- 多人协作风险高:不同人部署时,细节动作可能不一致。
所以,古法部署适合作为过渡方案,但一般不应该成为团队长期的最终形态。
十、后续更值得投入的方向
当团队规模、发布频率和协作复杂度逐步提高后,部署方式通常也会继续演进。后续更值得深入建设的方向包括:
- 流水线部署:将拉代码、安装依赖、构建、发布、重启统一接入 CI/CD 流程。
- 自动化脚本部署:把原本手工输入的命令收敛成一套标准脚本,减少人为失误。
- 自研 CLI 部署工具:结合团队自身业务特点,封装成统一的部署命令、环境选择、发布检查与回滚能力。
这些方向的目标并不是“看起来高级”,而是让发布过程更加稳定、可追踪、可复用。
十一、总结
真实工作中的前端部署,很多时候并没有想象中那么“云原生”或者“全自动”。更多时候,它就是一套扎实但朴素的操作链路:
从远程获取代码变化,切换到主分支,拉取最新代码,安装依赖并执行 Vite 打包,然后将新的 dist 替换到实际运行位置,再重启 Docker 容器,最后打开线上页面验证结果。
理解这套流程的意义,不只是为了完成一次上线,更是为了搞清楚前端产物究竟是如何从本地代码一步步变成线上页面的。
先把“古法部署”做稳,后面再去推进流水线、自动化脚本和自研 CLI,很多事情反而会更顺畅。

