配置 grunt-contrib-htmlmin 移除空白与布尔属性

解读

面试官抛出这道题,核心意图是验证候选人是否真正在生产环境用过 grunt-contrib-htmlmin,而不仅仅“跑通过 demo”。
国内前端部署普遍要求极致体积优化(SEO 评分、CDN 流量计费、首屏指标),空白字符(空格、换行、制表符)与冗余布尔属性(如 checked="checked")是 HTML 体积膨胀的隐形元凶
能否在 Gruntfile 里一次性正确配置 collapseWhitespace 与 removeRedundantAttributes,直接决定上线包大小能否再降 3%~8%,这是量化可考核的绩效点
此外,面试官还会旁敲侧击:

  1. 是否知道布尔属性在 XHTML5 与 HTML5 的差异
  2. 是否能在**多环境(dev、test、prod)**下开关该优化,避免开发环境可读性骤降;
  3. 是否理解**collapseWhitespace 的“保守模式”与“激进模式”**对 inline SVG、pre、textarea 的影响。
    答不到这三层,只能算“会用”,不算“敢上线”。

知识点

  1. collapseWhitespace: true 时,grunt-contrib-htmlmin 会安全合并相邻空白字符,但保留 pre/textarea/script 标签内格式;若需连换行都删掉,需再开 conservativeCollapse: false。
  2. removeRedundantAttributes: true 时,默认只删“值与属性名相同”的布尔属性(如 checked="checked"),不会误伤 data-xxx 或自定义属性;若页面需兼容 XHTML 规范,必须同步开启 removeEmptyAttributes,否则可能出现 checked="" 这种无效残留。
  3. Grunt 目标级覆盖:通过 grunt.option('env') 或 process.env.NODE_ENV,在 prod 目标里强制开启上述两项,在 dev 目标里关闭,保证本地调试可读。
  4. 与 grunt-contrib-watch 联动:htmlmin 任务必须晚于文件合并(grunt-contrib-concat)早于文件哈希(grunt-filerev),否则 watch 触发顺序错误会导致缓存指纹失效
  5. 国内 CDN 缓存策略:很多云厂商(阿里云、腾讯云)默认忽略 html 文件缓存,若 htmlmin 后体积下降明显,可主动提升 Cache-Control max-age,减少回源流量,这是可以写进季度 OKR 的量化收益

答案

在 Gruntfile.js 中,先确保已安装 grunt-contrib-htmlmin 0.2.0 及以上版本(低版本无 conservativeCollapse 选项),然后按环境区分配置:

module.exports = function(grunt) {
  grunt.initConfig({
    htmlmin: {
      options: {
        collapseWhitespace: true,               // 移除空白字符
        conservativeCollapse: false,            // 激进模式,连换行一起删
        removeRedundantAttributes: true,        // 删除布尔属性冗余值
        removeEmptyAttributes: true,            // 同步清理空属性,防 XHTML 残留
        keepClosingSlash: false                 // HTML5 无需自关闭斜杠
      },
      prod: {
        files: [{
          expand: true,
          cwd: 'dist/html',
          src: '**/*.html',
          dest: 'dist/html'
        }]
      },
      dev: {                                     // 开发环境可读性优先
        options: {
          collapseWhitespace: false,
          removeRedundantAttributes: false
        },
        files: '<%= htmlmin.prod.files %>'
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-htmlmin');

  grunt.registerTask('build', function() {
    var env = grunt.option('env') || process.env.NODE_ENV || 'dev';
    if (env === 'prod') {
      grunt.task.run(['concat:html', 'htmlmin:prod', 'filerev']);
    } else {
      grunt.task.run(['concat:html', 'htmlmin:dev']);
    }
  });
};

上线验证:

  1. 执行 grunt build --env=prod
  2. ls -lh dist/html 对比前后大小,一般可降 4%~10%
  3. 抽样 diff 检查,确保 pre、code、svg 标签内格式未被误杀
  4. 通过 curl -H "Accept-Encoding: gzip" https://cdn.xxx.com/index.html | wc -c 再次验证Gzip 后节省依旧明显,才算闭环。

拓展思考

  1. 与 grunt-usemin 的集成陷阱:若项目采用 usemin 自动注入构建块,htmlmin 必须在 usemin 之后运行,否则 usemin 的正则匹配会被压缩后的换行缺失干扰,导致路径替换失败
  2. 国内移动端特殊场景:微信内置浏览器对checked、disabled、selected 布尔属性有样式级 hack,若页面依赖 [checked] 选择器,需关闭 removeRedundantAttributes 或加 ignoreCustomComments 白名单,否则样式会掉。
  3. 量化收益汇报模板
    “通过 grunt-contrib-htmlmin 开启 collapseWhitespace + removeRedundantAttributes,首屏 HTML 体积由 42.3 kB 降至 38.1 kB,Gzip 后 12.4 kB → 11.2 kB,CDN 流量月省 21 GB,成本下降 180 元/月,SEO 评分 Lighthouse 95 → 98。”——把技术细节转成财务数字,是晋升答辩的杀手锏