项目优化-vite打包优化

问题来源:在被问到如何进行打包优化时候,常常会大脑一时宕机,特地找到一些资料去了解一些打包的优化方式。

概要

Vue3 + Vite 打包优化核心要点:

  1. 体积优化:开启压缩 / 混淆、利用 Tree Shaking、移除无用代码;
  2. 拆包优化:第三方依赖分包 + 路由懒加载,减少首屏加载体积;
  3. 资源优化:图片压缩 / 格式转换、CSS 抽离,降低静态资源体积;
  4. 速度优化:禁用生产环境 sourcemap、优化依赖预构建,提升打包速度。

这些策略可根据项目规模灵活调整,小型项目只需基础优化(压缩、Tree Shaking),中大型项目需配合拆包、CDN 进一步提升性能。

一、基础体积优化(核心)

1. 开启压缩与混淆

Vite 内置了压缩能力,无需额外安装插件,直接在配置中开启即可减小打包体积。

javascript 复制代码
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  build: {
    // 开启代码混淆(生产环境默认开启,可显式配置)
    minify: 'terser', // 替代默认的 esbuild,压缩效果更好
    terserOptions: {
      compress: {
        drop_console: true, // 移除 console.log
        drop_debugger: true // 移除 debugger
      }
    },
    // 开启 gzip 压缩(需配合服务器开启 gzip)
    reportCompressedSize: true
  }
})

2. Tree Shaking 深度优化

Vite 天生支持 Tree Shaking:

前提条件:确保代码符合ESM 规范, 避免无效代码被打包**(基于编译型确认依赖关系)**

错误条件:CommonJS 是基于运行时确认依赖关系,不适合

  • 优先使用 ESM 格式的依赖(如 lodash-es 替代 lodash):

    javascript 复制代码
    // 推荐(只打包用到的 debounce 方法)
    import { debounce } from 'lodash-es'
    // 不推荐(打包整个 lodash)
    import _ from 'lodash'
  • 避免在 setup 中定义未使用的变量 / 方法:

    javascript 复制代码
    <script setup>
    // 未使用的变量会被 Tree Shaking 移除
    const unusedVar = 123 // 打包时会被删除
    const handleClick = () => { /* 业务逻辑 */ } // 用到则保留
    </script>

3.vite-plugin-compression :给打包文件 "瘦身"(生成压缩包)

核心作用

这个插件的核心是在 Vite 打包时,自动为静态资源(JS/CSS/HTML)生成 Gzip/Brotli 压缩包,配合服务器端的压缩配置,让浏览器加载更小的文件,提升网络加载速度。

为什么需要它?

  • 手动生成压缩包效率低,插件可自动化完成;
  • Gzip/Brotli 压缩能让文件体积减少 60%-80%(比如 1MB 的 JS 文件压缩后可能只有 200KB);
  • 服务器可直接返回压缩包,无需实时压缩(节省服务器资源)。

实战配置 & 使用步骤

1. 安装
bash 复制代码
npm i vite-plugin-compression -D
# 或
yarn add vite-plugin-compression -D
2. 配置(vite.config.ts)
javascript 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import viteCompression from 'vite-plugin-compression'

export default defineConfig({
  plugins: [
    vue(),
    // 开启压缩
    viteCompression({
      verbose: true, // 打印压缩结果
      disable: false, // 是否禁用
      threshold: 10240, // 文件大于 10KB 才压缩(太小的文件压缩收益低)
      algorithm: 'gzip', // 压缩算法:gzip/brotliCompress
      ext: '.gz', // 压缩包后缀
      // 可选:同时生成 Brotli 压缩包(比 Gzip 压缩率更高)
      // algorithm: 'brotliCompress',
      // ext: '.br'
    })
  ]
})
3. 打包效果

执行 npm run build 后,dist 目录下会多出 .gz 后缀的压缩文件:

4. 服务器配合(关键)

生成压缩包后,需要配置 Nginx/Apache 让服务器优先返回压缩包:

复制代码
# Nginx 配置示例
http {
  gzip_static on; # 开启静态压缩包读取
  gzip_types text/plain text/css application/javascript application/json;
}

适用场景

  • 中大型项目(静态资源体积大,压缩收益明显);
  • 追求极致加载速度的场景(如官网、电商首页);
  • 服务器性能有限,不想实时压缩的场景。

打包结果:

4.rollup-plugin-visualizer:打包体积 "体检仪"(分析体积占比)

核心作用

这个插件的核心是生成可视化的打包体积分析报告,以图表(饼图 / 树形图)形式展示:

  • 每个依赖 / 模块占总打包体积的比例;
  • 哪些文件体积过大(比如某个第三方库占了 50% 体积);
  • 有无冗余代码 / 重复依赖。

为什么需要它?

  • 优化打包体积的前提是找到问题,这个插件能精准定位 "体积大头";
  • 面试中可体现你 "有数据支撑的优化思路",而非盲目优化;
  • 避免无用依赖 / 重复依赖占用体积(比如同时引入 lodash 和 lodash-es)。

实战配置 & 使用步骤

1. 安装
复制代码
npm i rollup-plugin-visualizer -D
# 或
yarn add rollup-plugin-visualizer -D
2. 配置(vite.config.ts)
javascript 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
  plugins: [
    vue(),
    // 体积分析插件
    visualizer({
      open: true, // 打包后自动打开报告页面
      filename: 'stats.html', // 报告文件名称(生成在 dist 目录)
      gzipSize: true, // 显示 Gzip 压缩后的体积
      brotliSize: true, // 显示 Brotli 压缩后的体积
    })
  ]
})
3. 使用效果

执行 npm run build 后:

  • 自动生成 dist/stats.html 文件;
  • 自动打开浏览器,展示可视化报告(示例效果):
    • 饼图:不同模块的体积占比(比如 element-plus 占 30%,vue 占 15%);
    • 树形图:可展开查看每个文件的具体体积;
    • 能看到哪些依赖是 "冗余" 的(比如重复引入的工具库)。

适用场景

  • 打包体积过大,想定位问题时;
  • 优化前后对比(比如优化后看体积是否下降);
  • 排查 "为什么打包后有 2MB" 这类问题;
  • 面试中展示 "数据驱动的优化思路"。

二、拆包优化(解决单文件体积过大)

1. 拆分第三方依赖(vendor 分包)

将 Vue、Element Plus 等第三方库单独打包,利用浏览器缓存:

javascript 复制代码
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { splitVendorChunkPlugin } from 'vite'

export default defineConfig({
  plugins: [
    vue(),
    // 自动拆分第三方依赖为独立 chunk
    splitVendorChunkPlugin()
  ],
   // 分包配置
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          // 基础框架:Vue 全家桶
          vue: ['vue', 'vue-router', 'pinia'],
          // UI 组件库:Element Plus
          ui: ['element-plus'],
          // 时间工具:dayjs(单独拆,体积小但常用)
          utils: ['dayjs'],
          // 可视化:echarts(体积大,必须单独拆!)
          echarts: ['echarts']
        },
        // 静态资源哈希配置(保留你的原有配置)
        chunkFileNames: 'js/[name]-[hash].js',
        entryFileNames: 'js/[name]-[hash].js',
        assetFileNames: '[ext]/[name]-[hash].[ext]'
      }
    }
  }
})

2. 路由懒加载(页面级拆包)

目的:减少主包大小,只有在用到组件的时候再去加载,提升性能。

结合 Vue Router 实现路由懒加载,只加载当前页面的代码:

javascript 复制代码
import type { RouteRecordRaw } from 'vue-router'
import Dashboard from '@/views/dashboard/index.vue'
import UserCenter from '@/views/user-center/index.vue'
import UserDetail from '@/views/user-center/UserDetail.vue'

// 路由规则
const routes: RouteRecordRaw[] = [
  {
    path: '/',
    redirect: '/dashboard'
  },
  {
    path: '/dashboard',
    name: 'Dashboard',
    // component: () => import('@/views/dashboard/index.vue'),
    component: Dashboard,
    meta: {
      title: '仪表盘'
    }
  },
  {
    path: '/user-center',
    name: 'UserCenter',
    // component: () => import('@/views/user-center/index.vue'),
    component: UserCenter,
    meta: {
      title: '用户中心'
    },
    children: [
      {
        path: 'detail/:id',
        name: 'UserDetail',
        // component: () => import('@/views/user-center/UserDetail.vue'),
        component: UserDetail,
        meta: {
          title: '用户详情'
        }
      }
    ]
  }
]

export default routes

2.2.1 不采用懒加载,查看打包体积

2.2.2 采用懒加载,缩小主包体积

三、资源优化(图片 / 字体 / 样式)

1. 图片压缩与格式转换

使用 vite-plugin-imagemin 压缩图片,自动转换为 WebP/AVIF 格式:

bash

bash 复制代码
# 安装依赖
npm i vite-plugin-imagemin -D

javascript

javascript 复制代码
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import viteImagemin from 'vite-plugin-imagemin'

export default defineConfig({
  plugins: [
    vue(),
    viteImagemin({
      // 图片压缩配置
      gifsicle: { optimizationLevel: 3 },
      optipng: { optimizationLevel: 7 },
      mozjpeg: { quality: 80 },
      pngquant: { quality: [0.8, 0.9] },
      // 自动转换为 WebP
      webp: { quality: 80 }
    })
  ],
  // 配置图片小于 4kb 时转为 base64
  build: {
    assetsInlineLimit: 4096
  }
})

2. CSS 抽离与压缩

Vite 自动抽离 CSS,但可配置优化:

javascript

javascript 复制代码
// vite.config.js
export default defineConfig({
  build: {
    // 禁用 CSS 内联(抽离为单独文件)
    cssCodeSplit: true,
    // CSS 压缩(默认开启)
    minify: true
  },
  // 配置 CSS 预处理器(减少冗余)
  css: {
    preprocessorOptions: {
      scss: {
        // 全局引入变量,避免重复导入
        additionalData: '@import "@/styles/variables.scss";'
      }
    }
  }
})

四、预构建优化(提升开发 / 打包速度)

1. 优化依赖预构建

Vite 会预构建 CommonJS 依赖为 ESM,可配置排除 / 包含特定依赖:

javascript 复制代码
// vite.config.js
export default defineConfig({
  optimizeDeps: {
    // 强制预构建指定依赖
    include: ['lodash-es', 'element-plus'],
    // 排除不需要预构建的依赖
    exclude: ['vue']
  }
})

2. 禁用 sourcemap(生产环境)

2.1 sourcemap 核心是什么?

sourcemap 本质是一个映射文件(.map 后缀),它记录了「打包 / 压缩后的代码」和「原始源代码」之间的对应关系。

举个通俗的例子:

你写的源代码是这样的(清晰易读):

javascript 复制代码
// src/main.js(原始代码)
const sayHello = () => {
  console.log('Hello Vue3')
  error() // 故意写个错误
}
sayHello()

经过 Vite 打包压缩后,代码变成了这样(一行式、变量名被混淆):

javascript 复制代码
// dist/assets/main.abc123.js(打包后代码)
const a=()=>{console.log("Hello Vue3"),error()};a();

如果运行时报错,浏览器会提示「错误在 main.abc123.js 的第 1 行第 30 列」------ 你根本不知道对应原始代码的哪一行。

但如果开启了 sourcemap,浏览器会读取 .map 文件,直接告诉你:错误在 src/main.js 的第 4 行第 3 列,这就是 sourcemap 的核心价值。

2.2 sourcemap 什么时候用?

场景 是否开启 sourcemap 原因
开发环境(npm run dev) ✅ 必须开 方便调试代码、定位错误
生产环境(npm run build) ❌ 建议关 1. 生成的 .map 文件会增大打包体积;2. 防止源码泄露(安全性);3. 提升打包速度

2.3 Vite 中如何配置 sourcemap?

javascript 复制代码
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  build: {
    // 生产环境禁用 sourcemap(核心优化点)
    sourcemap: false, 
    // 开发环境 Vite 会默认开启 sourcemap,无需额外配置
  },
  // 也可单独配置开发环境的 sourcemap
  server: {
    sourcemap: true
  }
})

2.4 sourcemap 的优缺点

优点:
  1. 调试时能直接定位到原始源代码的行号,大幅提升调试效率;
  2. 即使代码被压缩 / 混淆,也能清晰看到代码逻辑。
缺点:
  1. 生成 .map 文件会增加打包体积(比如 1MB 的 JS 会多出 2-3MB 的 .map 文件);
  2. 生产环境暴露 .map 文件可能导致源码泄露(攻击者可通过 .map 还原你的源代码);
  3. 增加打包时间(生成 .map 文件需要额外计算)。

总结

  1. sourcemap 是 "代码映射文件":关联打包后代码和原始源码,方便调试定位错误;
  2. 开发环境必开,生产环境必关:开发时提升调试效率,生产时减少体积、避免源码泄露;
  3. Vite 中通过 build.sourcemap 配置:生产环境设为 false 是核心打包优化点。

3. 去除console和debugger

一、为什么要移除?

  1. 减少代码体积 :大量 console.log 会增加 JS 文件体积(尤其是循环 / 高频函数里的日志);
  2. 避免敏感信息泄露 :比如 console.log(token) console.log(用户信息) 可能泄露隐私 / 业务数据;
  3. 提升执行效率console 本身是同步操作,高频执行会轻微阻塞主线程;
  4. 规范生产环境代码debugger 会强制中断代码执行,若不小心提交到生产环境会导致页面卡死。

二、Vite 中移除的两种方式(推荐第二种)

方式 1:手动删除(不推荐)

开发时写了日志,打包前逐行删除 ------ 效率低,容易漏删,不适合团队协作。

方式 2:自动移除(推荐,打包时自动清理)
javascript 复制代码
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  build: {
    // 生产环境禁用 sourcemap(核心优化点)
    sourcemap: false, 
    // 开发环境 Vite 会默认开启 sourcemap,无需额外配置
  },
  // 也可单独配置开发环境的 sourcemap
  server: {
    sourcemap: true
  },
  // 配置 esbuild 移除 console/debugger
  esbuild: {
    drop: ['console', 'debugger'] // 核心配置!
  },
})

三、验证是否生效

  1. 执行 npm run build 打包;
  2. 打开 dist 目录下的 JS 文件,搜索 console.log/debugger
  3. 若完全找不到,说明配置生效。

五、进阶优化(可选)

1. 使用 CDN 引入大型依赖

将 Vue、Element Plus 等通过 CDN 引入,不打包进项目:

javascript

javascript 复制代码
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  // 配置外部依赖(不打包)
  resolve: {
    alias: {
      '@': '/src'
    }
  },
  build: {
    rollupOptions: {
      external: ['vue', 'element-plus'], // 排除外部依赖
      output: {
        // 配置全局变量(对应 CDN 引入的全局对象)
        globals: {
          vue: 'Vue',
          'element-plus': 'ElementPlus'
        }
      }
    }
  }
})

然后在 index.html 中引入 CDN:

html

html 复制代码
<!-- index.html -->
<head>
  <script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/element-plus/dist/index.full.min.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/element-plus/dist/index.min.css">
</head>

2. 分析打包体积

使用 rollup-plugin-visualizer 分析打包体积,定位大文件:

bash

bash 复制代码
# 安装依赖
npm i rollup-plugin-visualizer -D

javascript

javascript 复制代码
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
  plugins: [
    vue(),
    // 打包后生成体积分析报告(dist/stats.html)
    visualizer({
      open: true, // 自动打开报告
      filename: 'stats.html'
    })
  ]
})
相关推荐
林夕sama2 小时前
多线程基础(五)
java·开发语言·前端
我叫蒙奇2 小时前
husky 和 lint-staged
前端
kyriewen2 小时前
JavaScript 继承的七种姿势:从“原型链”到“class”的进化史
前端·javascript·ecmascript 6
穷鱼子酱2 小时前
ElSelect二次封装组件-实现分页(下拉加载、缓存)、回显
前端
科科睡不着2 小时前
拆解iOS实况照片📷 - 附React web实现
前端
前端老兵AI2 小时前
Electron 桌面应用开发入门:前端工程师的跨平台利器
前端·electron
胖子不胖2 小时前
浅析cubic-bezier
前端
reasonsummer2 小时前
【办公类-133-02】20260319_学区化展示PPT_02_python(图片合并文件夹、提取同名图片归类文件夹、图片编号、图片GIF)
前端·数据库·powerpoint
胡耀超2 小时前
Web Crawling 网络爬虫全景:技术体系、反爬对抗与全链路成本分析
前端·爬虫·python·网络爬虫·数据采集·逆向工程·反爬虫