Skip to content

Vite 打包 Vue3 生产项目体积优化完整指南

更新: 6/28/2026 字数: 0 字 时长: 0 分钟

Vite 打包体积优化指南封面

体积优化的收益排序很明确:先砍掉根本不该打进来的大依赖,再做按需加载切分首屏,最后压榨压缩和细节。 下面按收益从高到低排列,每项给出配置、原理、适用场景和坑。所有配置基于 Vite 5/6 + Vue 3 正式版,构建器为 Rollup。

预期压缩比例是经验区间,实际收益取决于项目现状——重依赖、未做分包的项目收益偏上限,已优化过的项目收益偏下限。

优先级一:依赖瘦身(预期减 30%~60%,收益最高)

体积问题八成出在依赖上。先解决这块,性价比远高于抠配置。

1.1 按需引入组件库与工具库

配置代码(以 Element Plus + lodash 为例):

ts
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

export default defineConfig({
  plugins: [
    AutoImport({ resolvers: [ElementPlusResolver()] }),
    Components({ resolvers: [ElementPlusResolver()] }),
  ],
})

lodash 改用按方法引入或换 lodash-es

ts
// ❌ 全量引入,整包 70KB+
import _ from 'lodash'
// ✅ 按需,只打进用到的方法
import debounce from 'lodash-es/debounce'

生效原理:组件库全量引入会把所有组件和样式打进 bundle,按需引入让 Tree-shaking 只保留实际用到的部分。lodash-es 是 ESM 版本,支持 Tree-shaking,而 CommonJS 版 lodash 无法摇树。

适用场景:任何用了 Element Plus / Ant Design Vue / lodash / moment 这类大库的项目,几乎必做。

注意事项moment 体积巨大且无法摇树,建议直接换成 day.js(约 2KB),收益立竿见影。

1.2 用更轻的替代品

排查 package.json 里的重量级依赖,能换则换:

  • momentdayjs
  • echarts 全量 → echarts/core + 按需引入图表和组件
  • 完整 axios 在简单场景可考虑原生 fetch

生效原理:从源头减少打进来的代码量,比任何后置压缩都有效。

注意事项:替换涉及 API 改动,评估改造成本,做好回归测试。

优先级二:代码分割与懒加载(预期首屏减 20%~40%)

代码分割与路由懒加载

这类优化不一定减小总体积,但显著降低首屏加载体积,对用户感知最直接。

2.1 路由懒加载

配置代码

ts
const routes = [
  // ✅ 动态 import,每个路由独立 chunk,按需加载
  { path: '/dashboard', component: () => import('@/views/Dashboard.vue') },
  { path: '/settings', component: () => import('@/views/Settings.vue') },
]

生效原理:动态 import() 是 Vite/Rollup 的代码分割触发点,每个被动态引入的模块会被打成独立 chunk,只在访问对应路由时才加载。

适用场景:所有多路由 SPA,是性价比最高的首屏优化。

2.2 大组件 / 重依赖异步化

ts
import { defineAsyncComponent } from 'vue'
// 富文本编辑器、图表、地图等重组件按需加载
const HeavyEditor = defineAsyncComponent(() => import('@/components/HeavyEditor.vue'))

适用场景:编辑器、图表、PDF 预览等使用频率低但体积大的组件。

2.3 手动分包 manualChunks

配置代码

ts
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vue: ['vue', 'vue-router', 'pinia'],
          echarts: ['echarts'],
        },
      },
    },
  },
})

生效原理:把稳定的第三方依赖拆成独立 chunk,与业务代码分离。业务代码频繁变更,依赖很少变——分开后用户只需重新下载变动的业务 chunk,依赖 chunk 命中长期缓存。

注意事项:分包不是越碎越好。过度拆分会增加 HTTP 请求数和 chunk 间公共代码重复。优先按"变更频率"和"体积"分组,别为拆而拆。

优先级三:Tree-shaking 与压缩(预期再减 15%~30%)

Tree-shaking 与压缩混淆

3.1 生产构建压缩(默认已开,确认配置)

Vite 生产构建默认用 esbuild 压缩,速度快。要更极致的体积可换 terser:

ts
export default defineConfig({
  build: {
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,   // 移除 console
        drop_debugger: true,  // 移除 debugger
      },
    },
  },
})

生效原理:minify 做变量名压缩、空白删除、死代码消除。drop_console 在生产环境清掉调试日志,既减体积又避免信息泄露。

注意事项:terser 比 esbuild 压得更小但构建更慢,按需取舍。drop_console 会清掉所有 console,确认没有依赖 console 输出的逻辑。

3.2 Gzip / Brotli 预压缩

配置代码

ts
import viteCompression from 'vite-plugin-compression'

export default defineConfig({
  plugins: [
    viteCompression({ algorithm: 'gzip' }),
    viteCompression({ algorithm: 'brotliCompress', ext: '.br' }),
  ],
})

生效原理:构建时预生成 .gz/.br 文件,服务器直接下发压缩产物,省去运行时压缩开销。Brotli 压缩率通常比 Gzip 高 15%~20%。

适用场景:传输体积优化的关键一步。注意事项:需要 Nginx 等服务端配合开启对应支持(gzip_static on / Brotli 模块),否则预压缩文件不生效。

3.3 确保 Tree-shaking 生效

注意事项:Tree-shaking 依赖 ESM 静态结构。避免引入纯 CommonJS 库、避免有副作用的全量导入。第三方库的 package.jsonsideEffects: false 标记能帮助摇树,自己的工具库也建议正确标注。

优先级四:静态资源优化(预期减 5%~20%)

4.1 图片压缩与格式转换

ts
import viteImagemin from 'vite-plugin-imagemin'
// 构建时压缩 png/jpg/svg;优先用 WebP/AVIF 格式

小图片(< 4KB)Vite 默认转 base64 内联,减少请求;大图保持外链并压缩。

4.2 SVG 雪碧图 / 按需引入

图标用 vite-plugin-svg-icons 做雪碧图,避免每个图标一个请求或全量打包图标库。

4.3 控制资源内联阈值

ts
build: {
  assetsInlineLimit: 4096, // 小于 4KB 的资源内联为 base64
}

注意事项:内联阈值调太大反而会让 JS chunk 膨胀、丢失缓存优势,保持默认 4KB 附近即可。

优先级五:构建配置细节(预期减 0%~10%)

5.1 关闭生产环境 sourcemap

ts
build: { sourcemap: false }  // 生产默认即 false,确认别误开

sourcemap 文件很大,生产环境若不需要线上调试就关闭(或用 hidden 只上传错误监控平台)。

5.2 设置合理的浏览器目标

ts
build: { target: 'es2015' }  // 目标越新,polyfill 越少,体积越小

注意事项:target 要匹配实际用户的浏览器范围。定太新老浏览器报错,定太旧 polyfill 冗余,按用户画像权衡。

5.3 移除未使用的 CSS

配合 vite-plugin-purgecss 或 UnoCSS 的按需生成,清理未使用的样式。注意事项:PurgeCSS 容易误删动态拼接的类名,配好白名单并充分测试。

打包体积分析与结果验证

打包体积分析与验证

优化前先量化现状,优化后再对比验证,否则全凭感觉。

用 visualizer 看体积构成

ts
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
  plugins: [
    visualizer({ open: true, gzipSize: true, brotliSize: true }),
  ],
})

构建后生成 treemap 可视化报告,方块大小直观反映每个依赖的体积占比。先看哪个方块最大,就先优化谁——这是定位问题的第一手段。

验证流程

  1. 优化前:跑一次 vite build,记录 dist 总体积、各 chunk 体积、gzip 后体积,并保存 visualizer 报告。
  2. 逐项优化:每做一项改动单独构建,对比体积变化,确认收益和没引入功能问题。
  3. 优化后:对比首屏加载的 chunk 总和(不是 dist 总和——懒加载会让 dist 总量不变但首屏变小)。
  4. 真实环境验证:用 Chrome DevTools 的 Network 面板,在生产构建下看首屏实际传输体积(Transferred,即 gzip/brotli 后的大小)和加载瀑布流,这才是用户真正下载的量。

关键提醒:体积优化要看首屏传输体积,而非 dist 目录总大小。代码分割让总量不降反升都有可能,但首屏体积明显下降,用户体验才是真改善了。

优化方案速览(按收益排序)

优先级优化项预期收益关键手段
依赖瘦身30%~60%按需引入、换轻量库、moment→dayjs
代码分割懒加载首屏 20%~40%路由懒加载、异步组件、manualChunks
Tree-shaking + 压缩15%~30%terser、Gzip/Brotli、ESM 摇树
静态资源优化5%~20%图片压缩、WebP、SVG 雪碧图
构建配置细节0%~10%关 sourcemap、target、清 CSS

执行顺序建议:先 visualizer 量化现状 → 砍依赖 → 切分懒加载 → 配压缩 → 抠资源和细节 → 再次 visualizer 对比验证。