问题场景
一个中型 Vue3 + TS 项目(约 200 个页面),CI/CD 流程中每次 npm run build 耗时 15-25 秒。团队每天合并 20+ PR,每次合并都要重跑一遍构建,流水线排队严重。更糟的是,50% 的改动只是改了一个文案或样式,却要全量重新打包,浪费大量算力和时间。
原因分析
Vite 开发模式基于 esbuild + 浏览器原生 ESM,快得飞起。但生产构建走的是 Rollup 的完整打包流程:
- 没有利用缓存 ------ 默认情况下 Vite/Rollup 不持久化缓存
- 重复包体积计算 ------ 每次构建都对
node_modules中的依赖重新 tree-shake - chunk 文件名每次都变 ------ 即使代码没改,hash 变了,CDN 缓存失效
解决方案
方案一:Vite 缓存插件------让构建结果可复用
安装 vite-plugin-cache 插件,它会在构建时生成 .vite-cache 目录,下次构建时跳过未改动文件的 bundle 过程。
bash
npm i -D vite-plugin-cache
ts
// vite.config.ts
import { viteCache } from 'vite-plugin-cache'
export default defineConfig({
plugins: [
vue(),
viteCache({
cacheDir: '.vite-cache',
// 排除有副作用的入口文件
exclude: ['src/main.ts'],
}),
],
})
效果:未改动模块直接复用 cache,二次构建时间减少 60%-70%。
方案二:手动缓存 Rollup 构建产物(CI 场景)
在 CI 中持久化 Rollup 的 chunk 信息,利用 output.manualChunks 把第三方依赖打成一个稳定 chunk。
ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks(id) {
// 把 node_modules 中的依赖按包名分组
if (id.includes('node_modules')) {
// 大包拆成独立 chunk,减小变化传播面
if (id.includes('vue')) return 'vendor-vue'
if (id.includes('echarts') || id.includes('antv')) return 'vendor-charts'
if (id.includes('lodash')) return 'vendor-libs'
return 'vendor-others'
}
},
// 关键:内容不变则 hash 不变
entryFileNames: 'assets/[name]-[hash].js',
chunkFileNames: 'assets/[name]-[hash].js',
},
},
},
})
配合 CI 的缓存策略(GitHub Actions 的 actions/cache):
yaml
- uses: actions/cache@v3
with:
path: |
node_modules
**/.vite-cache
key: ${{ runner.os }}-vite-${{ hashFiles('yarn.lock') }}
效果:依赖包没变 → vendor chunk hash 不变 → CDN 复用旧文件 → 用户无需重新下载。
方案三:用 esbuild 替代 terser 做 minify
ts
export default defineConfig({
build: {
minify: 'esbuild', // 默认 terser,改为 esbuild
// terser 慢但体积小一点,esbuild 快但体积大 5%-10%
// 对于非极端场景,esbuild 完全够用
},
})
对比数据(实测 200 页面项目):
| 方案 | 首次构建 | 二次构建(改 1 个文件) | 备注 |
|---|---|---|---|
| 默认配置 | 18s | 18s | 每次全量 |
| + manualChunks | 16s | 16s | 依赖稳定 |
| + vite-plugin-cache | 15s | 4.2s | 模块级缓存 |
| + esbuild minify | 12s | 2.1s | 组合最优 |
方案四:增量构建跑单元测试而非全量构建
还有一个常见的误区------很多团队在 CI 里跑完构建才跑测试。改为:
vitest跑单测(不需要构建)- 检查
git diff,只对改动文件跑 E2E - 最终只构建一次产物
bash
# CI 中这样跑,节省构建次数
npx vitest run --changed # 只跑改动文件相关的测试
要点总结
| 手段 | 收益 | 落地成本 |
|---|---|---|
| vite-plugin-cache | 降 60%+ 构建时间 | 加 1 行插件 |
| manualChunks 拆包 | 稳定性 + CDN 复用 | 30 行配置 |
| esbuild minify | 快 5-8 倍压缩 | 改一个参数 |
| CI 缓存 | 跳过 node_modules 下载 | 标准 GitHub Action |
最佳实践组合 :manualChunks 分 vendor + vite-plugin-cache 做增量 + esbuild 做 minify + CI 缓存 node_modules,一套下来二次构建平均 2-3 秒。