离线缓存迁移:从已淘汰的 AppCache 到 Service Worker
本文目录
Application Cache(AppCache)曾用 HTML manifest 描述离线资源,但它已被弃用并从现代浏览器移除。继续保留 html manifest 属性既不能保证离线可用,也会让缓存更新行为难以预测。新项目应按资源类型组合 HTTP 缓存、Service Worker 与 Cache API;它们不是同一个机制的三个名字。
先拆分缓存职责
浏览器 HTTP cache 由响应头、验证器和请求语义控制,适合静态文件与可重新获取的响应。Cache API 是站点脚本显式管理的 Request/Response 存储;Service Worker 则能拦截受控页面的请求并决定网络或缓存响应。Service Worker 需要安全上下文,通常是 HTTPS;本地开发的 localhost 是受信任例外。
迁移前列出每类资源:带内容哈希的 JS/CSS、页面导航、API 数据、用户私有内容、图片/字体和离线兜底。不要把登录接口、个性化 HTML 或其它用户数据塞进可长期复用的公共缓存。缓存策略要与响应的 Cache-Control、Vary、用户身份和登出清理逻辑一致。
最小 Service Worker 生命周期
安装阶段可预缓存少量应用壳资源,激活阶段清理明确属于本应用且已废弃的缓存名,fetch 阶段按请求目的选择策略。下面只是教学骨架:上线前还应校验同源、失败回退、缓存配额、版本升级以及多标签页更新体验。
const CACHE_NAME = 'app-shell-v3';
const APP_SHELL = ['/offline.html', '/assets/app.css', '/assets/app.js'];
self.addEventListener('install', (event) => {
event.waitUntil(caches.open(CACHE_NAME).then((cache) => cache.addAll(APP_SHELL)));
});
self.addEventListener('activate', (event) => {
event.waitUntil(caches.keys().then((names) => Promise.all(
names.filter((name) => name.startsWith('app-shell-') && name !== CACHE_NAME)
.map((name) => caches.delete(name)),
)));
});
预缓存清单中任一资源不可用时,安装可能失败;发布前确认资源确实存在、状态成功且响应可缓存。不要无条件删除同源下其他应用的缓存。skipWaiting 与 clients.claim 会改变已有标签页的控制时机,必须评估页面版本混用和正在填写表单的风险。
选择策略,而不是一把梭
稳定的带哈希静态资产可使用 cache-first,并由新版本使用不同 URL;经常更新且可容忍旧数据的内容可采用 stale-while-revalidate;必须及时反映权限或库存的 API 通常 network-first 或直接 network。网络失败时给出有边界的离线回退,不能把任意失败响应永久存起来。
缓存大小受浏览器配额和淘汰策略影响,Cache API 不等于永久磁盘。对用户数据还要考虑多账号切换、退出登录、权限变更、过期删除、跨标签页一致性和浏览器站点数据清理。更新失败应保留上一个可用版本,别让安装一半的新缓存覆盖正常页面。
迁移验收
- 移除 manifest 属性并检查线上 HTML/脚本不再注册旧 AppCache。
- 使用新浏览器配置验证首次安装、断网访问、更新和安装失败回退。
- 验证用户登录/退出、不同账号切换及私有 API 不会串缓存。
- 检查缓存键、缓存版本、HTTP 响应头、站点存储配额与手动清理入口。
- 在浏览器 DevTools 的 Service Workers、Cache Storage、Network 面板检查控制与命中。
参考:Chrome AppCache Audit、MDN PWA Caching、MDN Service Worker API、MDN Cache API、MDN Service Worker security。
Live Notes
留言提交后会先进入审核,审核通过后对其他读者公开。