引言
Vite 的「快」不只体现在本地开发,在 CI 里同样可以通过缓存与并行把构建压到几十秒。但 CI 环境与本地不同——没有 node_modules、没有 .vite 缓存、依赖每次重新安装。本文系统讲 Vite 项目的 CI/CD:先搭一条标准的 GitHub Actions 流水线(安装 → 测试 → 构建 → 部署),再给依赖缓存、构建缓存与分片并行的优化手段,接着讲产物交付(带 hash 的构建产物如何部署到 Vercel / Netlify / GitHub Pages / 对象存储),最后给构建失败排查清单。
前置:/vite-vitest-testing/(测试 CI 集成)、/vite-env-production-best-practices/(生产构建)。CI 原理见 [[devops]]、[[github-actions]]。
目录
- 1. CI 与本地构建的差异
- 2. 标准流水线:安装、测试、构建、部署
- 3. 依赖缓存:pnpm/npm/yarn 命中
- 4. Vite 构建缓存与 esbuild 缓存
- 5. 并行 Job 与测试分片
- 6. 构建产物交付:哈希、上传与校验
- 7. 自动化部署:Vercel、Netlify、GitHub Pages 与对象存储
- 8. 多环境部署:预览分支与灰度
- 9. 构建失败排查清单
- 10. 速查表
- 延伸阅读
1. CI 与本地构建的差异
CI 环境 vs 本地的关键差异:
| 维度 | 本地 | CI |
|---|---|---|
| node_modules | 常驻 | 每次重装 |
| 缓存 | 有(.vite) | 无(需配置) |
| 网络 | 本地 | 沙箱(可受限) |
| 资源 | 固定 | 按配额 |
| 环境变量 | 本地 .env | CI secrets |
CI 构建的目标:快 + 可复现 + 可交付——所以一切围绕「缓存命中 + 确定性」做文章。
心智:CI 优化 = 把本地「已有」的东西(依赖、.vite、安装缓存)通过缓存带回 CI——命中即快。
2. 标准流水线:安装、测试、构建、部署
一条最基础的 Vite CI 流水线(GitHub Actions):
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm' # 依赖缓存(自动)
- name: 安装依赖
run: pnpm install --frozen-lockfile
- name: 类型检查
run: pnpm typecheck
- name: 单元测试
run: pnpm test
- name: 构建
run: pnpm build
- name: 上传产物
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
记忆:一条流水线 = checkout → 装依赖(缓存)→ 检查/测试 → 构建 → 上传产物——产物可复用给部署 Job。
3. 依赖缓存:pnpm/npm/yarn 命中
pnpm 的缓存原理:内容寻址存储(store),锁文件不变则命中。
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm' # actions 自动用 lockfile 做 key
缓存 key 设计:
- name: 缓存 pnpm store
uses: actions/cache@v4
with:
path: ~/.pnpm-store
key: pnpm-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
pnpm-${{ runner.os }}-
| 包管理器 | 缓存路径 | 命中关键 |
|---|---|---|
| pnpm | ~/.pnpm-store | lockfile hash |
| npm | ~/.npm | package-lock hash |
| yarn | ~/.cache/yarn | yarn.lock hash |
记忆:依赖缓存 key 用「锁文件 hash」——依赖没变则秒命中,变了才重装。
4. Vite 构建缓存与 esbuild 缓存
Vite 本身没有「持久构建缓存」,但可以缓存 esbuild 的产物目录:
Vite 构建是「源码转换 + Rollup 打包」,每次都重新执行。
但依赖的 esbuild 预构建产物(.vite/deps)可以缓存。
构建缓存策略:
- name: 缓存 Vite deps
uses: actions/cache@v4
with:
path: |
node_modules/.vite
key: vite-deps-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
优化构建的其他手段:
| 手段 | 效果 |
|---|---|
| 缓存 .vite/deps | 跳过依赖预构建 |
| 只构建必要 | 分支过滤(只 main 全量) |
| 并行独立 Job | 缩短墙钟时间 |
| 增量 | 无原生增量,靠分片 |
记忆:构建缓存主要缓存
node_modules/.vite(依赖预构建产物)——依赖不变则命中,大幅缩短安装后冷启动。
5. 并行 Job 与测试分片
并行 Job:把「测试」「构建」「Lint」拆成独立 Job,同时跑:
jobs:
lint:
runs-on: ubuntu-latest
steps: [checkout, setup, install, run lint]
test:
runs-on: ubuntu-latest
needs: [] # 不依赖 lint,并行
steps: [...]
build:
runs-on: ubuntu-latest
needs: [test] # 测试过才构建
steps: [...]
Vitest 分片(大测试集并行):
strategy:
matrix:
shard: [1, 2, 3]
steps:
- run: pnpm test --shard=${{ matrix.shard }}/3
| 并行策略 | 适用 | 代价 |
|---|---|---|
| Job 级并行 | 独立阶段(lint/test/build) | 重复装依赖 |
| 矩阵分片 | 测试集大 | 汇总报告 |
| 部署 Job | 需构建产物 | 依赖前序 |
记忆:并行分片缩短墙钟时间,但每个 Job 都要装依赖——权衡资源换时间,测试大用分片、阶段独立用 Job 并行。
6. 构建产物交付:哈希、上传与校验
Vite 产物天然带内容 hash(index-3f4k2a.js)——适合长期缓存与「只传变更」:
上传产物(Artifact):
- name: 上传 dist
uses: actions/upload-artifact@v4
with:
name: dist-${{ github.sha }}
path: dist/
retention-days: 7
下载并在部署 Job 使用:
- name: 下载产物
uses: actions/download-artifact@v4
with:
name: dist-${{ github.sha }}
path: dist/
产物校验(部署前 sanity check):
test -f dist/index.html && echo "✅ index.html 存在" || echo "❌ 构建不完整"
test -d dist/assets && echo "✅ assets 目录存在"
grep -q "/assets/" dist/index.html && echo "✅ 资源引用正确"
记忆:产物交付三件事——上传带 sha 名、部署 Job 下载、部署前校验完整性——防「构建过了但产物空」的翻车。
7. 自动化部署:Vercel、Netlify、GitHub Pages 与对象存储
Vercel(最省事,框架预设 Vite):
# vercel.json 可选(框架自动识别)
{
"buildCommand": "npm run build",
"outputDirectory": "dist",
"framework": "vite"
}
- name: 部署到 Vercel
uses: amondnet/vercel-action@v20
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.ORG_ID }}
vercel-project-id: ${{ secrets.PROJECT_ID }}
vercel-args: '--prod'
Netlify:
- name: 部署到 Netlify
uses: nwtgck/actions-netlify@v3
with:
publish-dir: './dist'
production-branch: main
deploy-message: "Deploy from CI"
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
GitHub Pages(需要 base 配置):
- name: 部署 Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist
# 注意:Pages 部署在 /<repo>/ 子路径 → vite.config 需 base: '/<repo>/'
对象存储(AWS S3 + CloudFront):
aws s3 sync dist/ s3://bucket/app --delete --cache-control "public,max-age=31536000,immutable"
aws cloudfront create-invalidation --distribution-id XXX --paths "/index.html"
记忆:部署三选一——Vercel/Netlify 省事、Pages 免费、对象存储可控;GitHub Pages 一定记得配
base。
8. 多环境部署:预览分支与灰度
Preview 分支:每个 PR 自动部署一个预览环境,验证后合并。
- name: Preview 部署
if: github.event_name == 'pull_request'
uses: amondnet/vercel-action@v20
with:
vercel-args: '--preview' # 非生产
多环境矩阵(staging/prod):
env:
VITE_API_BASE: ${{ vars.VITE_API_BASE }} # 环境级变量
# 构建时注入不同 API base
vite build --mode staging
vite build --mode production
| 环境 | 模式 | base | 用途 |
|---|---|---|---|
| PR 预览 | staging | staging-api | 联调 |
| 测试 | staging | staging-api | 验收 |
| 生产 | production | prod-api | 上线 |
记忆:环境通过 mode 切分、变量用 .env.[mode] 注入——一套代码多环境,靠构建参数而不是代码分支。
9. 构建失败排查清单
CI 构建失败十大原因与解法:
| 现象 | 原因 | 解法 |
|---|---|---|
pnpm install 慢 | 无缓存 | 加 actions/cache |
| 依赖装不上 | 网络/版本 | --frozen-lockfile 校验 |
| 内存 OOM | Rollup 大项目 | NODE_OPTIONS=--max-old-space-size=4096 |
| 构建产物不一致 | 环境变量缺失 | 检查 secrets/env |
| TypeScript 报错 | 类型不一致 | typecheck 步骤 |
| 测试失败 | 逻辑回归 | 看测试报告 |
| 部署 404 | base 配置错 | 检查 base 路径 |
| artifact 找不到 | Job 名/路径 | 对齐 upload/download |
| 超时 | 构建太慢 | 并行/分片/缓存 |
| secrets 缺失 | 未配置 | 仓库 → Settings → Secrets |
日志调试:
# 本地复现 CI 步骤
CI=1 npm run build # 模拟 CI 环境变量
npm run typecheck # 单独跑类型
记忆:CI 翻车先查「依赖/缓存/环境变量/内存」四大件——本地能复现就别怀疑 CI 玄学。
10. 速查表
| 需求 | 做法 |
|---|---|
| 基础流水线 | checkout → setup-node(cache) → install → build → upload |
| 依赖缓存 | actions/setup-node cache: 'pnpm' |
| 构建缓存 | cache node_modules/.vite |
| 并行 | Job 级拆分 + Vitest --shard |
| 产物交付 | upload-artifact 带 sha 名 |
| Vercel | vercel-action + secrets |
| Pages | gh-pages action + 配 base |
| 多环境 | --mode staging + .env.staging |
| 排查 | 先看依赖/缓存/env/内存 |
一句话记忆:CI 优化围绕缓存(依赖 + .vite)+ 并行(Job/分片)展开;流水线 = 安装→测试→构建→上传;部署 Vercel 省事、Pages 免费但配 base、对象存储可控;多环境靠 mode 注入、产物带 hash 永久缓存——CI 快且稳的秘诀全在这套清单里。
延伸阅读
- /vite-vitest-testing/ — 测试 CI 集成
- /vite-env-production-best-practices/ — 生产构建与部署
- /vite-build-optimization/ — 构建产物优化
- [[devops]] — CI/CD 与流水线
- [[github-actions]] — Actions 深入
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。