如何根据 Can I Use 数据动态移除过时前缀

解读

面试官抛出此题,核心想验证三件事:

  1. 你是否把 Grunt 当成“流程编排器”而非“黑盒工具”,知道该在哪个环节插入“智能后处理”逻辑;
  2. 你是否理解 Autoprefixer 的底层机制,并能把它与 Can I Use 的 JSON 数据流打通;
  3. 你是否兼顾国内场景(微信内核、360 安全、QQ 浏览器、国产系统)与官方浏览器列表的差异,做到“降级兼容但不冗余”。

一句话:不是问“怎么压缩 CSS”,而是问“怎么让 Grunt 在构建时实时判断哪些前缀已死并自动剔除,同时不破坏业务”。

知识点

  • Autoprefixer 基于 PostCSS,内部使用 browserslist 语义化查询,数据源正是 Can I Use 的 JSON 包(caniuse-lite)
  • Grunt 生态里官方插件 grunt-postcss 是标准桥梁,但默认只“加”前缀,不“减”;要“减”必须再配 postcss-unprefixcssnano 的 discardUnused 规则;
  • browserslist 查询语句支持 > 1% in CNiOS >= 9Chrome >= 49 等,国内项目务必加 in CN 限定,否则按全球份额会把早已淘汰的国产浏览器误判为“活着”;
  • Gruntfile 里可用 grunt.config.set('postcss.options.browsers', browserslist) 动态回写,实现“同一份构建脚本,dev 用宽松规则,prod 用严格规则”;
  • CI 场景下需锁死 caniuse-lite 版本号,防止上游数据更新导致构建产物突变;
  • 若公司内网隔离,需把 caniuse-lite 离线包提前打进私有 npm 仓库,并在 .npmrc 里配 @browserlist:registry=https://your.npm
  • Sourcemap 链路必须保留,方便测试同学回溯“哪一行前缀被删”;否则线上出样式事故难以定位。

答案

  1. 安装依赖

    npm i -D grunt-postcss autoprefixer postcss-unprefix caniuse-lite browserslist
    
  2. 在 Gruntfile.js 中注册任务

    module.exports = function(grunt) {
      // 1) 基于国内份额动态生成 browserslist
      const browsers = require('browserslist')([
        '> 1% in CN',
        'iOS >= 9',
        'Chrome >= 49',
        'not dead'
      ]);
    
      // 2) 配置 postcss 管道:先删再补,确保“只留必要”
      grunt.initConfig({
        postcss: {
          options: {
            processors: [
              require('postcss-unprefix')({ browsers }), // 先剔除过时
              require('autoprefixer')({ browsers, cascade: false }) // 再补最新
            ]
          },
          dist: { src: 'src/css/*.css', expand: true, ext: '.css' }
        }
      });
    
      grunt.loadNpmTasks('grunt-postcss');
      grunt.registerTask('css', ['postcss']);
    };
    
  3. 本地验证

    npx grunt css
    

    git diff 观察产物:

    • 旧代码 -webkit-border-radius: 5px 被整行删除;
    • 新代码只保留 border-radius: 5px微信内核仍需要的 -webkit-line-clamp
  4. 上线前加锁
    在 package.json 追加

    "resolutions": {
      "caniuse-lite": "1.0.30001546"
    }
    

    防止 CI 次日构建结果突变。

拓展思考

  • 双轨策略:给 H5 与小程序分别维护 browserslist.h5.jsbrowserslist.mini.js,在 Gruntfile 里用 grunt.option('target') 动态切换,一份源码两套前缀
  • 增量移除:结合 grunt-newer,只对变更文件跑 postcss,10 万行级别项目可节省 60% 构建时间
  • 合规审计:在 postcss 管道后插入自定义插件,把被删前缀列表写入 dist/prefix-deprecated.json,方便法务留痕;
  • 未来替代:当团队迁移到 Vite/Rollup 时,同一套 browserslist 配置可直接复用,只需把 grunt-postcss 换成 rollup-plugin-postcss,实现构建工具无痛升级