使用 grunt-bundle-analyzer 生成可视化依赖图谱
解读
在国内前端面试中,面试官提出“用 grunt-bundle-analyzer 生成可视化依赖图谱”并不是单纯考察你会不会装插件,而是想确认三件事:
- 你是否理解 Grunt 生态与 Webpack 生态的边界——grunt-bundle-analyzer 只是 Webpack Bundle Analyzer 的 Grunt 封装,底层仍依赖 Webpack Stats 文件;
- 你是否能在 老项目(Grunt 驱动) 里低成本地接入现代构建分析能力,而不推翻原有 Gruntfile;
- 你是否知道 如何解读可视化图谱并给出优化动作,把“会跑任务”升级为“能降包体”。
因此,回答时要先讲“让 Grunt 输出 stats.json”,再讲“让 grunt-bundle-analyzer 消费 stats.json”,最后讲“看图说话、落地优化”,形成完整闭环。
知识点
- Grunt 任务三件套:loadNpmTasks / registerMultiTask / this.options()
- Webpack Stats 文件:webpack --json > stats.json 的产出,包含 chunks、modules、reasons、size、issuer 等字段
- grunt-bundle-analyzer 本质:对 webpack-bundle-analyzer 的 Grunt 封装,不执行打包,仅起静态 HTTP 服务
- analyzerMode:server / static / disabled,国内 CI 场景常用 static,生成 report.html 丢到 nginx 目录供团队查看
- 默认端口 8888:云主机需检查安全组;本地若被占用可在 options 里改 port
- Gzip 体积 vs Parse 体积:图谱中 红色条为 parse size,灰色条为 gzip size,面试时要明确“以 gzip 为准评估网络传输,以 parse 为准评估运行时内存”
- SideEffects 与 UsedExports:图谱中 “[harmony side effect evaluation]” 提示可配 sideEffects: false 做 tree-shaking
- 拆分策略:若 moment/lodash 单体过大,可借 grunt-replace + babel-plugin-import 做按需加载,再回炉 grunt-webpack 重新生成 stats,对比两次图谱
答案
-
安装依赖
npm i -D webpack webpack-cli webpack-bundle-analyzer grunt-bundle-analyzer
注意:grunt-bundle-analyzer 4.x 要求 Node ≥ 14,老项目若卡在 Node 12 需锁 3.x 版本 -
改造 Gruntfile.js
在现有 grunt.initConfig 中追加:'bundle-analyzer': { prod: { options: { statsFile: './dist/stats.json', analyzerMode: 'static', reportFilename: './bundle-report.html', openAnalyzer: false, // CI 环境无浏览器 logLevel: 'info' } } }同时确保前置任务(如 webpack)已生成 stats.json:
grunt.registerTask('webpack-stats', function () { const done = this.async(); const { exec } = require('child_process'); exec('npx webpack --config webpack.prod.js --json > dist/stats.json', done); }); -
串联任务
grunt.registerTask('analyze', ['webpack-stats', 'bundle-analyzer']); -
本地运行
grunt analyze
成功后终端输出
Bundle Analyzer 静态报告已生成:bundle-report.html,总体积 1.34 MB(gzip 428 kB) -
解读图谱
- 发现 echarts 占 368 kB(gzip 124 kB),且整包被打入;
- 查看 reasons 发现
import echarts from 'echarts'未使用按需语法; - 给出优化:改用
import * as echarts from 'echarts/core'并手动引入 LineChart、CanvasRenderer,重新跑 grunt analyze,echarts 体积降至 92 kB(gzip 34 kB),整体主包减少 232 kB
-
集成到 CI
在 GitLab CI 中增加:analyze: stage: check script: - npm ci - grunt analyze artifacts: paths: - bundle-report.html expire_in: 7 days通过 artifacts 把报告挂在 MR 页面,MR 评论机器人自动读取 bundle-report.html 的 meta 标签 total-size,若增长 > 5% 则打回
拓展思考
-
无 Webpack 的老 Grunt 项目怎么办?
若项目仍用 grunt-contrib-uglify + concat 无模块化打包,则无法生成 stats.json。此时可:- 先用 grunt-rollup 或 grunt-webpack 把源码做一次 “伪打包”(仅走模块解析,不真发线上),拿到 stats 后再分析;
- 或者换 source-map-explorer,直接分析 concat 后生成的 .js.map,但粒度只能到文件级,无法看到 npm 包内部细节
-
如何横向对比两次迭代?
在 CI 里把 stats.json 当产物保存,下次构建用 webpack-bundle-diff 做二次开发,输出 “新增/删除/体积变化” 三列 Markdown 表格,贴到 MR 评论,让产品同学也能看懂 -
安全与权限
国内金融、政务项目禁止把源码 stats 上传到第三方平台。自建 analyzer 服务时,务必在 nginx 层加 basic-auth 并只开放 VPN 网段;report.html 里含完整路径名,上线前用 grunt-string-replace 把绝对路径脱敏为相对路径 -
性能红线
可视化报告本身也是静态资源,超过 5 MB 的 stats.json 会导致 analyzer 页面卡顿。此时可在 options 里加generateStatsFile: false, statsOptions: { all: false, assets: true, chunks: true, modules: true }只保留关键字段,把 stats.json 压到 800 kB 以内,确保在低端办公本上也能秒开