H5 Service Worker 离线缓存策略
H5 Service Worker 离线缓存策略
Service Worker 可以拦截页面请求并使用缓存响应,为弱网和离线场景提供能力。但缓存策略设计不当会让用户长期看到旧页面、接口数据串号,甚至新旧脚本互不兼容。离线能力应按资源类型选择策略,并通过明确版本和更新流程管理生命周期。
一、理解独立生命周期
Service Worker 与页面生命周期分开,主要经历安装、等待、激活和运行阶段。新版本下载完成后,可能要等旧页面关闭才激活。
因此,发布新版本不能假设所有页面立即由新逻辑控制。页面协议、缓存结构和消息格式需要考虑新旧版本短暂共存。
二、只缓存明确允许的内容
资源可以分为:
- 应用外壳:基础脚本、样式和固定图标。
- 页面内容:文章、商品列表等可更新数据。
- 用户数据:账户与个性化信息。
- 实时或敏感数据:不能使用过期结果的内容。
应用外壳适合版本化缓存;用户和关键业务数据必须按账户、有效期和安全要求处理,不能默认加入公共缓存。
三、为缓存设置版本
const CACHE_PREFIX = 'offline-demo-';
const SHELL_CACHE = `${CACHE_PREFIX}shell-v4`;
const CONTENT_CACHE = `${CACHE_PREFIX}content-v2`;
const ACTIVE_CACHES = new Set([SHELL_CACHE, CONTENT_CACHE]);
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(names => Promise.all(
names
.filter(name => (
name.startsWith(CACHE_PREFIX) && !ACTIVE_CACHES.has(name)
))
.map(name => caches.delete(name))
))
);
});
删除范围必须限于当前应用管理的缓存命名空间。真实项目如果还有其他功能缓存,应一起纳入版本清单,不能清空全部缓存。
四、缓存优先适合稳定资源
async function cacheFirst(request) {
const cache = await caches.open(SHELL_CACHE);
const cached = await cache.match(request);
if (cached) return cached;
const response = await fetch(request);
if (response.ok) {
await cache.put(request, response.clone());
}
return response;
}
带内容摘要的构建资源适合缓存优先,因为内容变化时资源标识也变化。没有版本标识的页面文档如果长期缓存优先,可能无法及时获得新入口。
五、网络优先适合更新内容
async function networkFirst(request, cacheName) {
const cache = await caches.open(cacheName);
try {
const response = await fetch(request);
if (response.ok) {
await cache.put(request, response.clone());
}
return response;
} catch (error) {
const cached = await cache.match(request);
if (cached) return cached;
throw error;
}
}
只有业务允许展示旧数据时,失败后才返回缓存。交易状态、余额等关键内容不能为了“有结果”而使用过期响应。
六、按请求类型路由策略
self.addEventListener('fetch', event => {
const request = event.request;
if (request.method !== 'GET') return;
if (isVersionedAsset(request)) {
event.respondWith(cacheFirst(request));
return;
}
if (isCacheableContent(request)) {
event.respondWith(networkFirst(request, CONTENT_CACHE));
}
});
分类函数应基于项目统一请求描述或确定规则。写操作不应直接缓存,也不能在离线时假装提交成功。
七、离线写入需要独立队列
如果产品允许离线编辑,应先把业务内容和待同步操作持久化,再由明确同步任务重放。队列需要稳定操作标识、顺序和冲突规则。
Service Worker 只能提供执行机会,不能代替幂等协议。支付、下单等非幂等操作不应无条件后台重放。
八、更新提示不要强制打断
新 Worker 准备好后,页面可以提示用户在合适时机刷新。强制立即接管可能让当前表单和旧脚本状态不兼容。
确需立即更新的安全修复,也要先保存允许恢复的草稿,并清楚告知用户。是否跳过等待应由发布策略决定,不是所有版本的默认选项。
九、控制缓存容量与清理
浏览器存储空间有限,缓存可能被系统清理。离线页面不能把缓存当作永久唯一数据源。
内容缓存应设置数量或时间边界,账户退出后精确清理个人数据。清理逻辑不能只在安装阶段执行,因为用户可能长期不更新版本。
十、测试离线矩阵
至少验证:
- 首次访问时完全离线。
- 已缓存后离线刷新。
- 新旧 Worker 同时存在。
- 新资源发布后能否正确更新。
- 缓存内容过期和容量淘汰。
- 用户切换账户后数据不串用。
- 写操作离线时显示真实状态。
- 缓存缺失时错误页面可理解。
总结
Service Worker 离线能力依赖清晰的资源分类。版本化外壳使用缓存优先,允许旧数据的内容采用网络优先,敏感与实时数据遵循更严格策略。再配合缓存清理、平滑更新和幂等离线队列,才能避免“离线可用”变成“长期陈旧”。
- 点赞
- 收藏
- 关注作者
评论(0)