Vite 构建缓存优化:二次构建从 15s 降到 2s 的实战方案

问题场景

一个中型 Vue3 + TS 项目(约 200 个页面),CI/CD 流程中每次 npm run build 耗时 15-25 秒。团队每天合并 20+ PR,每次合并都要重跑一遍构建,流水线排队严重。更糟的是,50% 的改动只是改了一个文案或样式,却要全量重新打包,浪费大量算力和时间。

原因分析

Vite 开发模式基于 esbuild + 浏览器原生 ESM,快得飞起。但生产构建走的是 Rollup 的完整打包流程:

  1. 没有利用缓存 ------ 默认情况下 Vite/Rollup 不持久化缓存
  2. 重复包体积计算 ------ 每次构建都对 node_modules 中的依赖重新 tree-shake
  3. 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 里跑完构建才跑测试。改为:

  1. vitest 跑单测(不需要构建)
  2. 检查 git diff,只对改动文件跑 E2E
  3. 最终只构建一次产物
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 秒

相关推荐
晓得迷路了1 小时前
栗子前端技术周刊第 135 期 - Vite 8.1、Rspack 2.1、Babel 8.0...
前端·javascript·vite
你听得到112 小时前
用户说 App 卡,但说不清在哪?我把 Flutter 监控 SDK 升级成了链路观测工作台
前端·flutter·性能优化
天渺工作室11 小时前
实现一个adblock/adblock plus等浏览器广告拦截器检测插件
前端·javascript
阳光是sunny11 小时前
Vue 项目怎么做用户行为全链路监控?轻量插件方案详解
前端·面试·架构
ZhengEnCi11 小时前
Q04-Vite禁用CSS代码分割-解决生产环境样式加载顺序混乱问题
前端·vue.js·vite
九酒12 小时前
AI Agent 开发踩坑记:口播功能非得用 APP 原生实现吗?
前端·人工智能·agent
Jackson__12 小时前
做了一段时间的AI coding后,我终于搞清了 CLI 和 MCP 的区别
前端·agent·ai编程
IT_陈寒15 小时前
JavaScript项目实战经验分享
前端·人工智能·后端