描述在 grunt 中实现 ETag 自动生成

解读

面试官想知道你是否能把“前端构建”与“服务端缓存策略”打通。Grunt 本身只负责构建产出,ETag 的生成逻辑必须前置到构建阶段完成,并把结果写进文件名或 JSON 映射表,供上线时做“静态资源版本化 + 强缓存”。回答时要体现三点:

  1. 选什么哈希算法;
  2. 如何把哈希注入文件名或 manifest;
  3. 上线流程怎样消费这份数据。
    国内大厂(阿里、腾讯、字节)的静态资源 CDN 链路普遍要求“文件指纹 + 超长缓存 + ETag 回源校验”,因此你必须证明方案能无缝接入他们的发布系统。

知识点

  • ETag 本质:HTTP 响应头,资源唯一标识,优先级高于 Last-Modified。
  • Grunt 插件体系:grunt-contrib-copy、grunt-filerev、grunt-assets-versioning、grunt-hash-manifest。
  • 哈希算法选型:CRC32 快但易碰撞,MD5 128 bit 平衡,SHA256 安全但 32 字节过长;国内静态资源域名常要求 8 位十六进制即可。
  • 文件名与 manifest 两种落地方式
    – 直接改文件名:main.3b5c7d2e.js,CDN 自动回源生成 ETag;
    – 不改文件名,生成 assets.json 映射,由 Nginx 或 Node 层读取并输出 ETag: "3b5c7d2e"
  • 并发与增量:grunt-newer 或 grunt-cache-breaker 避免重复算哈希,提升 10 倍构建速度。
  • 灰度与回滚:manifest 文件入 Git,回滚只需切换 commit,CDN 边缘节点 ETag 随之回退。

答案

我采用“文件指纹 + manifest 双保险”方案,核心步骤如下:

  1. 安装并加载插件
    npm i -D grunt-filerev grunt-hash-manifest grunt-contrib-clean
    
  2. Gruntfile.js 配置
    module.exports = function(grunt) {
      grunt.initConfig({
        clean: { dist: 'dist' },
        filerev: {
          options: { algorithm: 'md5', length: 8 },
          dist: { src: ['dist/**/*.{js,css,png}'] }
        },
        hash_manifest: {
          options: { format: 'json', dest: 'dist/assets.json' },
          src: 'dist/**/*'
        }
      });
      grunt.registerTask('etag', ['clean', 'filerev', 'hash_manifest']);
    };
    
  3. 构建产出
    运行 grunt etag 后,dist 目录出现 main.3b5c7d2e.js
    {"main.js":"main.3b5c7d2e.js","main.css":"main.a4f5e1b3.css"}
    
  4. 上线接入
    – 前端模板引擎读取 assets.json,输出 <script src="//cdn.xxx.com/main.3b5c7d2e.js">
    – CDN 回源时,对象存储自动把文件 MD5 作为 ETag 返回,无需后端再算一遍;
    – 若采用“文件名不变”策略,则在 Node 中间件里读取 assets.json,把 ETag: "3b5c7d2e" 写进响应头,同样达到缓存校验目的。
  5. 回滚
    Git 回退到旧 commit,assets.json 随之还原,CDN 边缘节点会重新拉取旧文件,ETag 自动匹配,用户无感知。

拓展思考

  1. 哈希维度升级:对 JS 做“code-splitting + chunkhash”,CSS 用“contenthash”,图片用“imagemin 后再 hash”,确保任意字节变动都触发新 ETag。
  2. 服务端渲染场景:Next.js、Nuxt 项目可把 assets.json 注入到 _document.js,在服务端直出 HTML 时一次性带齐 ETag,减少一次 RTT。
  3. SRI(Subresource Integrity)联动:在 grunt-filerev 后加 grunt-sri,生成 integrity="sha256-xxx",与 ETag 形成“双重摘要”,防 CDN 投毒。
  4. HTTP/2 Server Push:利用 assets.json 动态生成 Link: </main.3b5c7d2e.js>; rel=preload; as=script,把哈希带在响应头,避免推送过时资源。
  5. 边缘函数计算:阿里云 CDN + EdgeRoutine 可直接读取对象 ETag,若与 grunt 生成的 manifest 不一致,立即触发钉钉告警,实现“构建-发布-校验”闭环。