Set.prototype.difference()尚未被主流浏览器原生支持,生产环境需用Array.from().filter()或封装函数模拟;应基于内容哈希比对manifest版本清单,并注意HTTP缓存、哈希类型及并发安全。

目前 Set.prototype.difference() 尚未被主流浏览器原生支持(截至 2024 年底,仅 Chrome 120+ 和 Safari 17.4+ 实验性启用,且需开启 flag),因此**不能直接用于生产环境的前端增量更新逻辑**。但它的语义非常契合“计算待下载文件集合”这一场景——我们可以用等价的、已广泛兼容的方式实现相同效果,并借此设计健壮的增量更新机制。
用兼容写法模拟 difference() 计算待下载文件
假设服务端下发了本次版本所需的完整资源哈希清单(currentAssets),而本地已缓存部分资源(cachedAssets),两者均为 Set 实例(元素为文件路径或内容哈希):
- 用
Array.from(currentAssets).filter(key => !cachedAssets.has(key))得到差集数组 - 或封装成可复用函数:
const difference = (a, b) => new Set([...a].filter(x => !b.has(x)));
返回的是新Set,避免污染原始缓存 - 注意:确保两个 Set 元素类型一致(推荐统一用文件内容哈希字符串,而非 URL,避免因查询参数或重定向导致误判)
结合资源清单与版本元数据做精准比对
光比文件名不够,必须绑定版本上下文:
- 每次构建生成唯一
manifest.json,包含:{"version": "1.2.3", "assets": {"main.js": "a1b2c3...", "logo.png": "d4e5f6..."}} - 前端启动时读取本地存储的上一版 manifest(如有),提取其
assets值构建成cachedAssetsSet - 请求新 manifest 后,用上述
difference()模拟逻辑比对currentAssets与cachedAssets,得到真正需要拉取的文件列表 - 若本地无旧 manifest(首次安装/缓存清空),则全量下载
规避常见陷阱:缓存、哈希、并发
实际落地时这几个点容易出错:
立即学习“前端免费学习笔记(深入)”;
-
HTTP 缓存干扰:请求 manifest 时务必加时间戳或
cache: 'no-cache',否则可能拿到过期清单 - 哈希不匹配:确保构建工具(如 Webpack/Vite)输出的哈希是内容哈希(contenthash),不是 chunkhash 或 hash,否则相同代码不同构建会误判为变更
-
并发覆盖风险:多个 Tab 同时更新时,避免重复下载同一文件。可用
Promise.cache模式或 IndexedDB 锁机制协调 - 降级处理:差集为空时,仍建议校验关键文件(如入口 JS)是否真实可执行,防止本地缓存损坏
轻量替代方案:Service Worker + Cache API
如果目标是渐进式更新而非手动管理包,更推荐用标准能力:
- 在 Service Worker 中监听
fetch,对资源请求自动查 Cache Storage - 更新时调用
cache.keys()获取当前缓存键,再与新 manifest 对比,用cache.delete()清理废弃项,cache.add()加入新增项 - 优势:浏览器自动处理 HTTP 头、离线优先、无需手写差集逻辑,且兼容性极佳


















