描述在 grunt 中实现 ETag 自动生成
解读
面试官想知道你是否能把“前端构建”与“服务端缓存策略”打通。Grunt 本身只负责构建产出,ETag 的生成逻辑必须前置到构建阶段完成,并把结果写进文件名或 JSON 映射表,供上线时做“静态资源版本化 + 强缓存”。回答时要体现三点:
- 选什么哈希算法;
- 如何把哈希注入文件名或 manifest;
- 上线流程怎样消费这份数据。
国内大厂(阿里、腾讯、字节)的静态资源 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 双保险”方案,核心步骤如下:
- 安装并加载插件
npm i -D grunt-filerev grunt-hash-manifest grunt-contrib-clean - 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']); }; - 构建产出
运行grunt etag后,dist 目录出现main.3b5c7d2e.js及{"main.js":"main.3b5c7d2e.js","main.css":"main.a4f5e1b3.css"} - 上线接入
– 前端模板引擎读取 assets.json,输出<script src="//cdn.xxx.com/main.3b5c7d2e.js">;
– CDN 回源时,对象存储自动把文件 MD5 作为 ETag 返回,无需后端再算一遍;
– 若采用“文件名不变”策略,则在 Node 中间件里读取 assets.json,把ETag: "3b5c7d2e"写进响应头,同样达到缓存校验目的。 - 回滚
Git 回退到旧 commit,assets.json 随之还原,CDN 边缘节点会重新拉取旧文件,ETag 自动匹配,用户无感知。
拓展思考
- 哈希维度升级:对 JS 做“code-splitting + chunkhash”,CSS 用“contenthash”,图片用“imagemin 后再 hash”,确保任意字节变动都触发新 ETag。
- 服务端渲染场景:Next.js、Nuxt 项目可把 assets.json 注入到
_document.js,在服务端直出 HTML 时一次性带齐 ETag,减少一次 RTT。 - SRI(Subresource Integrity)联动:在 grunt-filerev 后加 grunt-sri,生成
integrity="sha256-xxx",与 ETag 形成“双重摘要”,防 CDN 投毒。 - HTTP/2 Server Push:利用 assets.json 动态生成
Link: </main.3b5c7d2e.js>; rel=preload; as=script,把哈希带在响应头,避免推送过时资源。 - 边缘函数计算:阿里云 CDN + EdgeRoutine 可直接读取对象 ETag,若与 grunt 生成的 manifest 不一致,立即触发钉钉告警,实现“构建-发布-校验”闭环。