如何对上传结果生成 manifest 并注入 HTML

解读

在国内前端工程化面试中,这道题考察的是**“构建产物版本化管理”“资源自动注入”**两大能力。
面试官真正想听的是:

  1. 你能否用 Grunt 把上传 CDN 后返回的 URL 列表固化成一份 manifest(通常是 JSON);
  2. 再把 manifest 中的指纹文件名自动注入到 HTML,确保用户拿到的是最新资源,同时保留灰度回滚能力。
    回答时务必体现**“国内 CDN 路径规则”“缓存击穿策略”“Grunt 插件链式协作”**这三点,否则会被认为“只会配置,不会落地”。

知识点

  • grunt-filerev:给静态资源加 md5 指纹,避免 CDN 缓存
  • grunt-upload-cdn(或自研插件):把带指纹的文件上传到国内云厂商(OSS、COS、KSS),返回“cdnPath+key”映射表
  • grunt-manifest-json:将上传结果写成 manifest.json,格式需包含文件逻辑名→CDN 绝对路径的键值对,方便服务端灰度
  • grunt-replacegrunt-inline-cdn:读取 manifest,把 HTML 里形如 __CDN__/js/index.js 的占位符替换成真实 CDN 地址
  • grunt-contrib-watch + grunt-contrib-livereload:开发阶段本地不注入 CDN,保证调试速度;仅在生产构建流程触发上传与注入
  • 版本回滚策略:manifest 文件随包发布到服务器,运维只需回滚 manifest 即可瞬间切换静态资源版本,无需重新上传 CDN

答案

  1. 安装核心链
npm i -D grunt-filerev grunt-upload-cdn grunt-manifest-json grunt-replace
  1. Gruntfile 关键片段
grunt.initConfig({
  filerev: {
    options: { algorithm: 'md5', length: 8 },
    dist: { src: ['dist/**/*.{js,css,png}'] }
  },
  upload_cdn: {
    dist: {
      options: {
        provider: 'aliyun-oss', // 国内主流
        accessKeyId: '<%= env.ALI_KEY %>',
        secretAccessKey: '<%= env.ALI_SECRET %>',
        bucket: 'static-xyz',
        region: 'oss-cn-hangzhou',
        // 上传成功后把结果写到 grunt.config('cdnMap')
        callback: function(result) {
          grunt.config.set('cdnMap', result);
        }
      },
      files: [{ expand: true, cwd: 'dist', src: '**/*' }]
    }
  },
  manifest_json: {
    dist: {
      options: { space: 2 },
      src: '<%= grunt.config("cdnMap") %>',
      dest: 'dist/manifest.json'
    }
  },
  replace: {
    html: {
      options: {
        patterns: [{
          match: /__CDN__\/([^'"\s]+)/g,
          replacement: function(_, logicName) {
            var map = grunt.file.readJSON('dist/manifest.json');
            return map[logicName] || grunt.fail.warn('找不到 '+logicName);
          }
        }]
      },
      files: [{ expand: true, cwd: 'dist', src: '*.html', dest: 'dist/' }]
    }
  }
});

grunt.registerTask('build', [
  'clean:dist',
  'filerev',
  'upload_cdn',
  'manifest_json',
  'replace:html'
]);
  1. 运行
ALI_KEY=xxx ALI_SECRET=xxx grunt build

构建结束后,dist 目录出现 manifest.json,HTML 中的 __CDN__/js/index.js 被替换成 //static-xyz.oss-cn-hangzhou.aliyuncs.com/js/index.3f4a2b8c.js,完成**“上传→生成→注入”**闭环。

拓展思考

  • 灰度发布:把 manifest.json 上传到配置中心(Nacos、Apollo),网关按用户维度下发不同版本 manifest,实现**“静态资源灰度”**,这是国内大厂常用方案
  • 多环境隔离:利用 grunt-env 区分 daily、pre、prod,不同环境对应不同 CDN 域名,避免测试资源污染线上缓存
  • 雪崩降级:若 CDN 异常,可在 manifest 里增加 fallback: true 字段,Node 中间件读取后自动把资源域名切回源站,保证可用性
  • 增量上传:grunt-upload-cdn 支持 etag 比对,只传变更文件,节省 70% 流量,适合日构建 1000+ 文件的大型业务
  • 合规审计:国内金融、政企项目要求“静态资源可溯源”,可在 manifest 中追加 gitCommit 字段,方便等保测评时快速定位版本