如何对包体积进行 KB 级预算

解读

在国内前端面试中,面试官问“KB 级预算”并不是想听“用 webpack-bundle-analyzer”这类通用答案,而是考察候选人能否在 Grunt 体系 里把“体积指标”当成 CI 门禁 来落地。
核心诉求有三点:

  1. 能在 Grunt 任务流 里实时采集各产物体积,粒度到 KB 甚至 Byte;
  2. 能把采集结果与 产品、运维、测试三方约定的阈值 做比对,超限即失败;
  3. 失败时必须给出 可定位到插件或文件的明细,方便秒级回滚或优化。
    答不出“Grunt 怎么做”会被直接判定为“只会 webpack 的简历党”。

知识点

  1. grunt-bytesize:官方体积统计插件,可输出 gzip 前后两种体积;
  2. grunt-asset-monitor:社区插件,支持把体积写进 JSON 并做阈值断言;
  3. grunt-contrib-uglifygrunt-contrib-cssminsourceMap 同步:确保统计的是最终产物而非中间文件;
  4. grunt.file.write + grunt.config.merge:动态把体积数据写回 Gruntfile,实现“自更新预算”;
  5. process.exit(1):在 Grunt 任务里手动触发非零退出码,让 Jenkins/GitLab CI 直接红灯;
  6. 国内网络差异:阿里、腾讯镜像源对相同版本插件打包后体积可能差 3%~5%,预算需留 冗余区间
  7. 政企合规场景:部分甲方要求 “单文件不超过 100 KB(gzip 后)” 写进合同,预算必须可审计。

答案

  1. package.json 里约定预算池:

    "bundleBudget": {
      "app.js":  "98",
      "vendor.js": "300",
      "total": "500"
    }
    

    单位 KB,数字字符串方便 Grunt 模板读取。

  2. 安装并加载三件套:

    npm i -D grunt-bytesize grunt-asset-monitor grunt-fail
    
  3. 在 Gruntfile 中注册 体积采集 任务:

    grunt.initConfig({
      bytesize: {
        all: {
          src: ['dist/**/*.js', 'dist/**/*.css'],
          options: {
            gzip: true,          // 国内 CDN 普遍开 gzip,必须以 gzip 为准
            maxSize: '<%= bundleBudget.total %>KB',
            reporter: 'json',    // 输出给下一步断言
            dest: '.tmp/size.json'
          }
        }
      }
    });
    
  4. 注册 预算断言 任务:

    grunt.registerTask('budget', function() {
      const budget = grunt.config('bundleBudget');
      const actual = grunt.file.readJSON('.tmp/size.json');
      let ok = true;
    
      Object.keys(budget).forEach(name => {
        if (name === 'total') return;
        const file = actual[name];
        if (!file) { grunt.log.error(`缺失 ${name}`); ok = false; return; }
        if (file.gzip > parseInt(budget[name], 10)) {
          grunt.log.error(`${name} 体积 ${file.gzip}KB 超预算 ${budget[name]}KB`);
          ok = false;
        }
      });
    
      if (!ok) {
        grunt.fail.fatal('包体积 KB 级预算未通过,CI 中断');
      }
    });
    
  5. 把顺序写进默认流,确保 任何优化插件之后 立即检查:

    grunt.registerTask('dist', [
      'clean:dist',
      'uglify:dist',
      'cssmin:dist',
      'bytesize',
      'budget',
      'compress'
    ]);
    
  6. GitLab CI 中增加缓存策略,避免每次 npm i 拉包差异导致体积抖动:

    cache:
      paths:
        - node_modules/
    script:
      - npm run grunt dist
    

落地后,MR 阶段即可看到 “budget” 红灯,点击日志直接定位到超预算文件,实现真正的 KB 级门禁

拓展思考

  1. 多环境差异化预算:预发环境加 source-map 体积可放宽 10%,生产环境再收紧;可通过 grunt.option('env') 动态切换阈值。
  2. 图片子预算:国内小程序对 主包 2 M 限制 极严,可把 grunt-contrib-imagemin 的压缩率写回同一 .tmp/size.json,用 “图片体积/代码体积” 占比 做二级门禁。
  3. 增量预算:结合 git diff --name-only,只对 变更文件 做体积比对,解决老项目历史包袱大、一次性全量优化成本高的问题。
  4. 审计留痕:把每次 .tmp/size.json 上传到 阿里云 OSS 指定目录,文件名带 commit-sha,实现 “体积基线”可追溯,方便政企客户过等保测评。