题干与适用场景
这是前端系统设计与状态管理交叉题。重点是把本地数据、同步队列、服务器版本和用户可见状态连接起来,而不是只说“加一个缓存”。
面试官考察什么
- 能否把本地草稿、待同步操作和服务器确认状态分开。
- 能否选择合适的冲突策略,并说明哪些字段不能自动合并。
- 能否处理重试、重复提交、浏览器关闭和多标签页。
- 能否让离线、同步中、冲突和失败状态可理解、可恢复。
回答前需要澄清的问题
先确认表单字段是否独立、是否有审计或金额等高风险字段;离线编辑是否必须支持附件;服务器是否提供版本号或操作日志;允许最后写入获胜,还是需要字段级合并或人工确认;以及浏览器兼容范围和本地数据保留期限。
30 秒回答框架
我会把 IndexedDB 作为本地持久层,把每次编辑记录成带 clientMutationId 和基线版本的操作。界面立即显示本地状态,同时标记待同步;Service Worker 或应用启动时批量发送队列。服务器用版本检查和幂等键回应成功、冲突或校验失败。可安全合并的字段自动处理,高风险字段展示差异并让用户选择,所有状态都能重试或撤销。
分步骤深入解答
1. 先定义状态模型
每份草稿至少有 serverVersion、localVersion、syncState 和 lastError。每个待发操作保存 clientMutationId、字段变更、创建时间和基线版本。读取优先来自本地存储,网络响应只在确认版本后合并,避免请求返回顺序覆盖较新的本地编辑。
2. 用 IndexedDB 保存可恢复事实
IndexedDB 适合保存较大的结构化离线数据,但需要处理 schema 升级、空间不足和浏览器清理。把草稿、操作队列和冲突记录分开存储,写入时使用事务保证草稿与队列同时落盘。敏感表单只保留必要字段,并在退出或过期时清除。
3. 设计同步触发与幂等协议
在线时可立即尝试发送,离线时进入队列;支持 Background Sync 时可注册唯一 tag 交给 Service Worker,不能把它当成所有浏览器都可靠提供的保证。每次请求携带 clientMutationId 和基线版本,服务端重复收到同一 id 时返回同一结果,不重复应用。
4. 让冲突策略匹配字段风险
标题、标签等可独立字段可以按字段版本合并;金额、权限和合规声明不能简单用最后写入获胜。服务器返回冲突时,界面并列显示本地值、服务器值和修改时间,用户选择后生成新操作。自动合并必须记录来源,不能默默覆盖。
5. 处理失败、重试和多上下文
网络失败使用指数退避并保留队列顺序;校验失败进入可编辑的错误状态,不要无限重试。多标签页用 BroadcastChannel 或版本检查通知彼此,避免两个页面同时发送旧操作。浏览器关闭后,已落盘队列下次启动继续处理,用户始终能看到待同步数量和最后一次错误。
高质量示范回答
我会先按字段风险和离线边界定需求。客户端以 IndexedDB 保存草稿与操作队列,每次操作带 clientMutationId 和基线 serverVersion;UI 先显示本地结果并标记待同步。在线或恢复连接后,应用或 Service Worker 发送队列,服务端用版本检查和幂等键返回成功、冲突或校验错误。独立字段可以自动合并,高风险字段则展示本地与服务器差异,让用户确认后再提交。网络失败可退避重试,业务失败进入可修改状态;多标签页通过版本通知避免旧数据覆盖。界面提供离线、同步中、冲突、失败和已保存的明确状态,不能依赖一个模糊的 loading 图标。
常见错误
- 只缓存最后一份表单,没有操作队列和版本基线。
- 用时间戳或最后写入获胜处理所有字段。
- 把 Background Sync 当成所有浏览器都会触发的保证。
- 重试没有幂等键,导致重复创建或重复扣款。
- 冲突只在控制台报错,用户不知道如何恢复。
- 忽略 schema 升级、存储配额、多标签页和浏览器关闭。
追问及应对
如果用户在两个标签页同时编辑怎么办?
每个标签页订阅版本变化,发现本地基线过期就提示合并;发送前再次做版本检查。共同的操作 id 仍由服务端去重。
离线数据会不会泄露?
按业务需要最小化本地字段,敏感内容加密并缩短保留时间;登出、设备共享或策略变化时清除本地数据。
如果浏览器不支持 Background Sync 呢?
以应用启动、页面重新获得焦点、网络状态变化和定时短轮询作为降级触发,并在界面说明仍有待同步记录。核心正确性不能依赖该 API。
什么时候允许自动合并?
只有字段语义独立、版本基线明确且业务可以接受暂时差异时才自动合并。金额、权限、库存或审计字段应要求显式确认。