网页端实现“断网也能用”的关键在于三层协同:Service Worker + Cache API 管理静态资源、IndexedDB 持久化业务数据、健壮同步机制保障网络恢复后安全回传,并通过字段级比对或乐观锁处理冲突。

网页端实现“断网也能用”的无缝体验,关键不是堆砌技术,而是让缓存和同步各司其职、彼此配合。核心思路是:用户感知不到网络状态变化——页面照开、操作照做、数据不丢。这需要三层协同:静态资源靠 Service Worker + Cache API 拦截并兜底;结构化业务数据靠 IndexedDB 本地持久化;网络恢复后靠健壮的同步逻辑把改动安全回传。
静态资源离线可用:用 Service Worker 管住页面骨架
用户打开网页的第一秒,不能等网络响应。必须把 HTML 页面结构、核心 CSS、JS 脚本、图标等提前缓存好。
- 注册 Service Worker 前确保使用 HTTPS(localhost 可豁免),首次注册后需刷新页面才能激活
- 在 install 阶段预加载关键资源,比如
['/', '/index.html', '/assets/app.js', '/styles/main.css'] - fetch 事件中采用 stale-while-revalidate 策略:先返回缓存内容保证秒开,再后台拉新版本更新缓存
- 避免缓存上传接口(如
/api/upload)或带敏感参数的请求,防止污染缓存
业务数据离线可读写:用 IndexedDB 存真实状态
光有页面不行,用户得能看列表、填表单、改待办。这些动态内容必须存在本地数据库里,而不是靠 localStorage 或 sessionStorage。
- IndexedDB 支持事务、索引、大容量存储,适合存带时间戳、状态字段的结构化记录
- 每条待同步数据必须含唯一 ID(推荐
crypto.randomUUID())、创建时间、同步状态(pending/sent/failed) - 文件类字段(如
)不能直接 JSON 序列化,应存文件元信息 +URL.createObjectURL()临时链接 - 数据库连接要在页面加载时初始化完成,别等到表单 submit 才去
indexedDB.open(),否则可能来不及存就跳转了
网络恢复后自动同步:不靠 online 事件赌运气
window.addEventListener('online') 只触发一次,且不可靠——设备唤醒、DNS 失败、服务器 503 时它都可能误报为“已联网”。真正可用的同步机制要更务实。
- 监听
online仅作提示信号,同步动作必须配合主动探测:比如发起一个轻量级健康检查请求(/api/health)确认服务可达 - 按时间戳或自增 ID 排序待同步记录,用
index.openCursor()遍历,逐条提交,失败则标记status: 'failed'并保留原始记录 - 同步成功后才更新状态为
'sent',并清理或归档;失败记录留着供人工重试或日志分析 - 对重复提交敏感的操作(如支付、下单),服务端需支持幂等设计(例如用客户端传来的 request_id 去重)
冲突处理不能回避:本地与服务端不一致时怎么办
用户离线改了 A 字段,别人在线把同一条记录的 B 字段也改了,网络恢复后怎么合并?硬覆盖会丢数据,全拒绝又影响体验。
- 服务端返回 409 Conflict 时,前端应拉取最新服务端版本,与本地修改做字段级比对
- 简单场景可用“最后写入 wins”策略(以时间戳为准),但需明确提示用户“他人已更新,你的修改将覆盖以下字段”
- 关键业务(如文档协作)建议引入向量时钟或 CRDT 算法,不过多数应用用带版本号的乐观锁更实际:每次更新附带
_version,服务端校验不匹配则拒绝并返回当前值 - 所有冲突决策都要留痕,至少记下本地修改时间、服务端版本、用户选择结果,方便后续审计











