当你完成了功能开发,决定上线之前,需要问一个实在的问题:用户下载的 JS 文件到底有多大?里面塞了什么? 肉眼翻 dist 目录只能看到一堆哈希文件名,真正要定位体积瓶颈,必须借助打包体积分析工具。
对于 Vue 项目,目前主流构建工具是 Vite(底层用 Rollup) 和 Webpack,它们各有对应的可视化分析插件。
rollup-plugin-visualizer(Vite / Rollup 项目)
Vite 生产构建基于 Rollup,rollup-plugin-visualizer 可以将打包产物的模块组成生成一个可交互的 HTML 图表,帮你快速揪出哪些模块占了最大体积。
1. 安装与配置
npm install rollup-plugin-visualizer --save-dev
在 vite.config.js 中引入并启用,建议仅在需要分析时开启,避免拖慢常规构建:
import { defineConfig } from 'vite'
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
// 其他插件...
visualizer({
open: true, // 构建完成后自动在浏览器打开报告
filename: 'stats.html', // 报告输出的文件名
gzipSize: true, // 显示 gzip 压缩后的大小
brotliSize: true, // 显示 brotli 压缩后的大小
})
]
})
运行 npm run build 后,项目根目录会生成 stats.html,浏览器打开后你看到类似这样的结构:
- 三种视图模式:树状图(Treemap)、列表(List)、网络图(Network)。通常用树状图,它像磁盘占用图一样,用面积表示模块体积。
- 颜色标识:不同颜色的方块对应不同的包,比如
node_modules下的第三方库、你自己的src源码。 - 交互操作:点击方块可以下钻查看该模块内部组成,快速定位具体是哪个文件/函数过大。
2. 从报告中锁定体积大户
打开报告后,最常见的问题是某个第三方库被打包进去了,且体积远超预期。例如:
- 发现
moment.js占了几百 KB,可以考虑换成dayjs或者使用moment的按需加载 locale 配置。 lodash整个引入会导致几百 KB 增加,应改为import { debounce } from 'lodash'或直接使用lodash-es实现 Tree Shaking。- UI 组件库(如 Element Plus)若没有按需引入,会全量打入,可通过
unplugin-vue-components实现自动按需引入。
3. 定位自己的业务代码膨胀
有时候体积大户不是 node_modules 而是你自己的代码。例如:
- 一个 Vue 组件里
import了一张未被压缩的 PNG,该图片被 inlined 成 Base64 打入 JS。 - 某个
data.js文件里硬编码了超大 JSON 数据(比如全国省市区列表)。 - 重复打包:同一个模块被多个入口引用而未做代码分割。
当发现这些问题后,就可以针对性地优化:图片改用外链或压缩,数据拆分成懒加载 JSON,将公共模块提取为 chunk。
webpack-bundle-analyzer(Webpack 项目)
如果你的项目还在用 Webpack(如基于 Vue CLI 搭建),webpack-bundle-analyzer 是事实标准。它同样提供交互式可视化报告。
1. 安装与配置
npm install webpack-bundle-analyzer --save-dev
在 vue.config.js 中通过 chainWebpack 配置,或直接修改 webpack.config.js:
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = {
chainWebpack: config => {
if (process.env.ANALYZE) { // 通过环境变量按需启动
config.plugin('webpack-bundle-analyzer')
.use(BundleAnalyzerPlugin, [{
analyzerMode: 'static',
reportFilename: 'bundle-report.html'
}])
}
}
}
在 package.json 中添加脚本,方便调起:
"scripts": {
"analyze": "cross-env ANALYZE=true npm run build"
}
运行后,报告会展示每个包的体积信息,而且支持查看经过代码压缩(minified)和 gzip 压缩后的大小,这更接近用户实际下载的尺寸。
2. 分析报告的关键动作
- 筛出重复模块:当同一库的不同版本被打进不同 chunk 时,报告会以红色标记,提示存在重复。比如
core-js的多个版本,或moment的 locale 资源被多次打包,需要统一版本或剔除非必要资源。 - 查看代码分割结果:确认路由懒加载是否生效。如果所有路由组件都集中在一个巨大的 chunk 中,说明动态
import()未正确配置,应立即修正。 - 评估异步 chunk 大小:单个 chunk 太大(例如超过 500KB)影响加载时间,可考虑进一步拆分路由或组件。
3. 使用分析结果驱动优化
拿到报告后,优化路径非常清晰:
| 问题现象 | 常见解决方案 |
|------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
| 某第三方库全量引入 | 替换为更小替代品(如 dayjs 替代 moment),或配置 Tree Shaking,或使用按需引入插件 |
| 图片/字体被打包进 JS | 将较大的静态资源改为单独文件,并使用 CDN 或 S3 分发,不在 JS 中内联 |
| 多个 chunk 包含同样模块 | 在 Webpack 中配置 splitChunks.cacheGroups 提取公共依赖;在 Vite 中通过 build.rollupOptions.output.manualChunks 手工划分 |
| 首屏入口 chunk 过大 | 检查是否把所有组件都同步 import 了;使用异步组件(defineAsyncComponent)或路由懒加载,配合 Suspense |
| 源码中某个无用 import 被打包 | 结合 Tree Shaking 检查是否有副作用 (side effects);在 package.json 中声明 "sideEffects": false,确保无副作用文件可被安全删除 |
实际使用建议
- 纳入构建流程,但按需开启:分析插件会拖慢构建速度,平时开发时不要常驻。可以在
package.json中增加build:analyze脚本,需要时跑一次。 - 关注 gzip 大小而非原始大小:用户下载的往往是 CDN 自动提供的 gzip 或 brotli 压缩文件,尽量将压缩后的大小作为判断标准。一般单文件 gzip 控制在 100KB 以内较为理想。
- 定期执行分析:项目迭代过程中,很容易不小心引入大依赖。建议在每个大版本发布前跑一次分析,尽早发现并解决体积膨胀。
- 结合懒加载与预加载:通过分析确认哪些 chunk 可以延迟加载后,还可以使用
<link rel="prefetch">或preload提示浏览器在空闲时预取可能需要的资源,进一步优化加载体验。
这两个工具是性能排查链的起点:能看见,才能优化。当你习惯在每次重大更新后看一眼打包报告,很多体积问题都会在萌芽阶段被解决,而不是等用户反馈“白屏太久了”才回过头去抓包。