前端打包体积一直降不下来?我靠这几款分析工具把产物体积砍了 60%
前端打包体积一直降不下来?我靠这几款分析工具把产物体积砍了 60%
项目越做越大,打包后的 JS 文件动不动就几百 KB、甚至几 MB。
打开页面白屏好几秒,LCP 标红,用户骂娘,产品经理天天蹲你工位。
我之前也这样。一个后台管理系统,首屏 JS 打包出来 1.8MB——你想想,用户打开一个管理后台,得先等他家浏览器默默下载 1.8MB 的 JS 文件,然后解析、执行,页面才能动弹。这体验谁受得了?
后来我学乖了:不要瞎优化,先拿数据分析。你都不知道哪块大,怎么知道该切哪块?
今天就把我压箱底的 Bundle 分析三板斧分享出来,看完你也能手起刀落。
先装个 X 光机:webpack-bundle-analyzer
如果你还在用 Webpack,那webpack-bundle-analyzer 就是你的第一站。
装它:
npm install --save-dev webpack-bundle-analyzer
配置也非常无脑:
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
module.exports = {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'server',
openAnalyzer: true,
generateStatsFile: true,
statsFilename: 'stats.json'
})
]
}
npm run build 之后,浏览器会弹出一个交互式的树状图(treemap),每个方块代表一个模块,面积越大体积越大。
我那次 1.8MB 的首屏 JS 一看就知道问题在哪了:
antd完整打包:850KB(实际上我就用了 5 个组件)moment.js:230KB(但项目用的其实是 dayjs)lodash完整引入:520KB(只用了个get和debounce)- 其他冗余代码加在一起:200KB+
你看看,真正属于业务代码的部分,其实还不到 300KB。剩下的全是第三方依赖没做好按需加载。
Vite 项目换 rollup-plugin-visualizer
如果你已经切换到 Vite 了(2026 年了应该没人在新项目里用 Webpack 了吧),对应的分析工具是 rollup-plugin-visualizer。
npm install --save-dev rollup-plugin-visualizer
在 vite.config.ts 里配置:
import { visualizer } from 'rollup-plugin-visualizer'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
visualizer({
open: true,
gzipSize: true,
brotliSize: true,
filename: 'stats.html'
})
]
})
它会生成一个可交互的 stats.html,支持 treemap、sunburst、network 三种视图模式。我比较喜欢 sunburst(环形图)——能直观看出依赖层级关系,哪个包是谁引进来的,一目了然。
Vite 用户有个常见误区:觉得 Vite 开发过程爽,打包体积就不用管了。错。Vite 底层用的还是 Rollup,所有 Webpack 的优化经验基本可以平移。
深度分析:source-map-explorer
webpack-bundle-analyzer 能看出大块,但如果你想知道某个大模块里面到底是哪段代码占空间,还得上 source-map-explorer。
npm install --save-dev source-map-explorer
配合生成 source-map 来分析:
{
"scripts": {
"analyze": "source-map-explorer 'dist/js/*.js'"
}
}
它会根据 source map 反向映射到原始源码,精确告诉你每个源文件的压缩前/压缩后/ gzip 后的大小。我特别喜欢用它来追那个”看起来没什么但怎么就这么大”的幽灵模块。
最有价值的优化方案
工具上完了,说点实际的。这些是我从多个项目里总结出来的、砍体积最狠的几个招。
1. 按需加载不是配置问题,是习惯问题
很多人的”按需加载”只做了 UI 组件库,但忽略了自定义工具函数库。
// ❌ 这样会全量引入
import { debounce, get, throttle, cloneDeep, uniqBy } from 'lodash'
// ✅ 只引入需要的
import debounce from 'lodash/debounce'
import get from 'lodash/get'
一个朋友的项目,光改 lodash 的引入方式,从全量换成按需,体积直接降了 400KB。
2. 别再用 moment.js 了
我知道,moment.js 的 API 确实好用。但它真的是一个230KB 的胖子。
| 库 | 体积(压缩前) | gzip |
| moment.js | 230KB | 70KB |
| dayjs | 6.5KB | 3KB |
| date-fns | 按需约 10KB | 4KB |
| Luxon | 60KB | 20KB |
我个人的选择:普通项目用 dayjs(API 和 moment 几乎一样),复杂国际化需求用 date-fns(函数式按需加载)。
3. Code Splitting 要”智能”
不要只扔一个 React.lazy 就完事。真正的 code splitting 要考虑:
// 好的拆分:按路由 + 按组件权重
const Dashboard = React.lazy(() => import('./pages/Dashboard'))
const Settings = React.lazy(() => import('./pages/Settings'))
// 更细的拆分:把非首屏的大组件单独拆出来
const DataChart = React.lazy(() => import('./components/DataChart'))
const PdfExport = React.lazy(() => import('./components/PdfExport'))
同时配合 Webpack 的 splitChunks:
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\/]node_modules[\/]/,
name: 'vendor',
chunks: 'all',
priority: 10
},
antd: {
test: /[\/]node_modules[\/]antd[\/]/,
name: 'antd',
chunks: 'all',
priority: 20
}
}
}
4. Tree Shaking 的隐藏开关
很多人以为 Webpack production 模式自动开启 Tree Shaking 就完事了。但其实有个关键配置常被忽略——sideEffects。
在 package.json 里加上:
{
"sideEffects": ["*.css"]
}
告诉 Webpack:除了 CSS 文件,其他文件都没有副作用,可以放心摇掉。
我还见过一个踩坑案例:某人文件夹里有一个 index.js 只导出了部分函数,但另一个模块引入了整个文件夹:
// ❌
import utils from './utils'
// ✅
import { formatDate } from './utils/formatDate'
前者破坏了 Tree Shaking,后者才能正确摇树。
真实的优化效果
上面说的这些手段,我在两个项目上完整走了一遍:
| 指标 | 优化前 | 优化后 | 变化 |
| 首屏 JS 体积 | 1.8MB | 680KB | -62% |
| LCP | 3.8s | 1.2s | -68% |
| TTI | 4.2s | 1.8s | -57% |
这些数字不是凭空来的。就是从上面说的分析工具入手,按数据说话,一步步砍出来的。
总结一下
不拿到分析报告就不要谈优化。拍脑袋说”这个包太大了”和真的看数据去砍,差别就是 10% 和 60% 的差距。
工具没多神秘:webpack-bundle-analyzer(Webpack)、rollup-plugin-visualizer(Vite)、source-map-explorer(深追)。建个分析脚本,每天构建完看一眼,把体积变化纳入 code review,项目就不会偷偷胖起来。
你的项目打包后首屏 JS 多大?去查查,跟我说说结果。
评论区
登录后可评论。












