识别重复请求需构造请求指纹,提取协议、域名、路径、方法、关键参数及标准化请求体生成唯一标识;用 Map 缓存进行中请求的 Promise,GET 可安全复用,POST/PUT 仅限幂等接口;配合缓存策略提升效果。
识别重复请求的关键在于请求指纹
浏览器原生不提供“去重”能力,service worker 需自行构造唯一标识来判断是否为同一请求。不能只比对完整 url,因为时间戳、随机参数、设备 id 等动态字段会导致相同逻辑的请求产生不同 url。推荐做法是:提取协议、域名、路径、方法、关键查询参数(如 id、type)和标准化后的请求体(仅限 get/post 且需预处理),拼接成哈希或字符串作为指纹。
例如:/api/order/status?id=123&t=1716508560422 和 /api/order/status?id=123&t=1716508561005 应视为同一请求;而 /api/user/123 与 /api/user/456 则必须区分。
用 Map 缓存进行中请求的响应 Promise
当 fetch 事件触发时,先生成指纹,再查内存 Map。若已存在对应 Promise,则直接复用;否则发起真实请求,并将 Promise 存入 Map,待响应返回后自动清理。
- 使用 Map 而非对象,避免原型污染与键类型限制
- Promise 必须是未 resolve 的原始实例,不能包装成新 Promise,否则无法共享状态
- GET 请求可安全复用;POST/PUT 建议仅对幂等接口启用(如带 idempotency-key 请求头)
- 超时或失败时需从 Map 中删除该指纹,防止后续请求被挂起
避免阻塞主线程与生命周期问题
Service Worker 是独立线程,但频繁读写 Map 或执行复杂哈希计算仍可能影响性能。建议:
- 指纹生成逻辑尽量轻量,优先用 URL.pathname + searchParams.get('id'),避免全量解析 body
- 不依赖异步操作(如 IndexedDB 查询)做去重判断——必须同步完成指纹比对
- 在 activate 事件中清空 Map,防止旧 SW 残留状态干扰新版本
- 对流式响应(如 text/event-stream)禁用去重,因其不可 clone 多次消费
配合缓存策略提升效果
单纯拦截并发请求只是第一步。更进一步的做法是:将首次请求的响应缓存下来,后续同指纹请求直接走 cache.match(),跳过网络。
- 缓存 key 应与指纹一致,确保匹配准确
- 对非幂等 POST,缓存前需确认服务端支持重放(如返回 201 Created + location header)
- 使用
caches.match(request, { ignoreSearch: true })匹配带噪声参数的请求 - 缓存失效策略建议按资源类型区分:API 数据设短 TTL,静态资源设长缓存

















