38. Offline-First App 离线优先应用
V1 — 1个用户:Service Worker离线缓存
Section titled “V1 — 1个用户:Service Worker离线缓存”你想做一个离线也能用的笔记页面,地铁上没网也能查看之前的内容。
你要解决什么
Section titled “你要解决什么”- Service Worker拦截网络请求
- Cache API缓存静态资源
- 离线时返回缓存内容
给AI的Prompt
Section titled “给AI的Prompt”用HTML+JS+Service Worker实现一个离线可访问的笔记页面:
功能:1. 主页面:显示笔记列表+编辑器2. Service Worker: - install事件:预缓存index.html, style.css, app.js - fetch事件:网络优先(network-first)策略 - 网络失败 → 返回缓存版本 - 缓存成功响应供下次离线用3. 笔记数据存localStorage4. 离线指示器:页面顶部显示"在线"/"离线"状态5. navigator.onLine监听网络状态变化6. 新建/编辑/删除笔记(全离线操作)
文件结构:- index.html(主页面)- sw.js(Service Worker)- app.js(应用逻辑)- style.css(样式)
技术:HTML+CSS+JavaScript, Service Worker, Cache API- Service Worker注册成功
- 首次访问后资源被缓存
- 断网后页面仍可加载
- 离线状态显示”离线”
- 离线时可新建/编辑笔记
- 恢复网络后状态更新为”在线”
你学到了什么
Section titled “你学到了什么”- Service Worker生命周期 → Module: PWA基础
- 缓存策略(network-first)→ Module: 离线存储
- 网络状态监听 → Module: 浏览器API
V2 — 10个用户:IndexedDB本地数据库
Section titled “V2 — 10个用户:IndexedDB本地数据库”笔记越来越多,localStorage不够用了,需要完整的本地数据库。
你要解决什么
Section titled “你要解决什么”- IndexedDB存储结构化数据
- 完整的CRUD操作
- 联网时标记待同步数据
给AI的Prompt
Section titled “给AI的Prompt”在V1基础上用IndexedDB替代localStorage:
IndexedDB:1. 数据库:NotesDB2. 对象仓库:notes - id(自增主键) - title, content, created_at, updated_at - sync_status: "synced" / "pending" / "conflict"3. 索引:按updated_at排序,按sync_status筛选
CRUD操作:1. 创建笔记 → sync_status = "pending"2. 编辑笔记 → 更新内容 + sync_status = "pending"3. 删除笔记 → 标记deleted=true + sync_status = "pending"(软删除)4. 查询:按更新时间倒序,支持标题搜索
同步标记:1. 每条记录有sync_status字段2. 所有本地操作标记为"pending"3. 同步队列:列出所有pending记录4. 界面显示待同步数量角标5. 联网时显示"同步"按钮(V3再实现实际同步)
前端改进:1. 笔记列表(标题+摘要+时间)2. Markdown编辑器3. 每条笔记旁显示同步状态图标4. 离线/在线指示器
技术:HTML+CSS+JavaScript, IndexedDB, Service Worker- IndexedDB正确创建数据库和仓库
- CRUD操作正常(全离线)
- sync_status正确标记
- 搜索功能正常
- 软删除不显示但数据仍在
- 待同步数量准确
你学到了什么
Section titled “你学到了什么”- IndexedDB API → Module: 浏览器存储
- 对象仓库和索引设计 → Module: 数据建模
- 同步状态标记 → Module: 离线优先架构
- 软删除模式 → Module: 数据管理
V3 — 100个用户:冲突解决(Last-Write-Wins)
Section titled “V3 — 100个用户:冲突解决(Last-Write-Wins)”多个用户需要同步笔记到服务器,但离线编辑可能产生冲突。
你要解决什么
Section titled “你要解决什么”- Go后端同步API
- 时间戳+版本号管理
- Last-Write-Wins冲突策略
给AI的Prompt
Section titled “给AI的Prompt”在V2基础上实现服务端同步:
Go后端:1. PostgreSQL表: - notes(id, user_id, title, content, version, updated_at, deleted) - version:每次更新+12. 同步API: - POST /api/sync — 客户端上传变更,服务端返回服务端变更 - 请求体:{changes: [{id, title, content, version, updated_at, deleted}]} - 响应体:{server_changes: [...], conflicts: [...]}
同步流程:1. 客户端收集所有pending记录2. 发送到服务端3. 服务端对每条记录: - 如果服务端无此记录 → 直接创建 - 如果客户端version == 服务端version → 接受更新,version+1 - 如果客户端version < 服务端version → 冲突4. 冲突解决(Last-Write-Wins): - 比较updated_at,较新的覆盖较旧的 - 被覆盖的版本记录到conflict_history5. 返回服务端有但客户端没有的变更
客户端同步:1. 联网时自动触发同步(也可手动)2. 收到server_changes → 更新本地IndexedDB3. 收到conflicts → 标记为已解决(LWW自动处理)4. 同步完成 → 所有pending改为synced
技术:Go+Gin, PostgreSQL, IndexedDB, Service Worker- 客户端变更正确上传到服务端
- 服务端变更正确同步到客户端
- 版本号匹配时正常更新
- 版本冲突时Last-Write-Wins生效
- 冲突历史有记录
- 同步后pending状态清除
- 删除同步正确
你学到了什么
Section titled “你学到了什么”- 同步协议设计 → Module: 分布式数据
- 版本号冲突检测 → Module: 乐观并发
- Last-Write-Wins策略 → Module: 冲突解决
- 离线同步架构 → Module: 离线优先
V4 — 1000个用户:增量同步与冲突UI
Section titled “V4 — 1000个用户:增量同步与冲突UI”笔记量大了全量同步太慢,需要只同步变化的部分,冲突让用户自己决定。
你要解决什么
Section titled “你要解决什么”- 增量同步(只传变更)
- 同步队列管理
- 冲突UI让用户手动选择
给AI的Prompt
Section titled “给AI的Prompt”在V3基础上实现增量同步和冲突UI:
增量同步:1. 客户端记录last_sync_at时间戳2. 同步时只发送last_sync_at之后变更的记录3. 服务端也只返回该时间之后的变更4. 减少传输数据量(从全量到增量)
同步队列:1. 客户端维护操作队列(IndexedDB的sync_queue仓库)2. 每次操作记录:{operation, record_id, timestamp, data}3. 同步时按队列顺序提交4. 队列处理完毕后清空5. 同步失败时队列保留,下次重试
冲突UI:1. 冲突检测:客户端version != 服务端version 且 都有修改2. 不再自动LWW,弹出冲突解决对话框3. 对话框显示: - 左侧:本地版本(高亮变化部分) - 右侧:服务端版本(高亮变化部分) - 选项:保留本地/保留服务端/手动合并4. 手动合并:编辑器中显示合并后内容,用户确认5. 解决后标记resolved,继续同步
断点续传:1. 大文件笔记分块同步2. 同步中断后从断点继续3. 进度指示器
技术:Go+Gin, PostgreSQL, IndexedDB, React- 增量同步只传输变更数据
- last_sync_at正确更新
- 同步队列按顺序处理
- 冲突对话框正确显示双方内容
- 保留本地/服务端选择生效
- 手动合并功能正常
- 同步失败后队列保留重试
你学到了什么
Section titled “你学到了什么”- 增量同步(delta sync)→ Module: 同步策略
- 操作队列设计 → Module: 消息队列
- 冲突解决UI设计 → Module: 用户体验
- diff算法基础 → Module: 算法
V5 — 1万个用户:CRDT无冲突合并
Section titled “V5 — 1万个用户:CRDT无冲突合并”用户在多设备编辑,冲突太频繁,需要自动合并不丢数据。
你要解决什么
Section titled “你要解决什么”- CRDT数据类型实现
- 自动合并无冲突
- 多设备同步
给AI的Prompt
Section titled “给AI的Prompt”在V4基础上实现CRDT自动合并:
CRDT基础类型:1. G-Counter(只增计数器): - 每个节点维护自己的计数 - 合并:取每个节点的最大值 - 用途:点赞数、浏览数2. LWW-Register(Last-Writer-Wins寄存器): - 值+时间戳 - 合并:取时间戳更大的值 - 用途:笔记标题、标签3. OR-Set(Observed-Remove集合): - 添加时附带唯一标签 - 删除时删除特定标签 - 合并:求并集后去除已删除标签 - 用途:笔记中的标签集合
文本CRDT(简化版):1. 每个字符有唯一ID(lamport时间戳+节点ID)2. 插入:在两个字符ID之间插入新字符3. 删除:标记字符为删除(墓碑)4. 合并:按字符ID排序,自动无冲突
多设备同步:1. 每个设备有唯一device_id2. 操作日志(operation log)记录所有CRDT操作3. 同步时交换操作日志4. 本地应用远端操作 → 自动合并5. 最终一致性保证
技术:Go+Gin, PostgreSQL, IndexedDB, CRDT库- G-Counter在多设备递增后合并正确
- LWW-Register取最新值
- OR-Set添加删除合并无冲突
- 文本编辑多设备同时修改后合并正确
- 操作日志同步完整
- 无需用户手动解决冲突
- 最终一致(所有设备收敛到相同状态)
你学到了什么
Section titled “你学到了什么”- CRDT原理与类型 → Module: 分布式数据结构
- Lamport时间戳 → Module: 逻辑时钟
- 最终一致性 → Module: CAP理论
- 无冲突合并 → Module: 协作编辑
V6 — 10万+用户:协作离线编辑
Section titled “V6 — 10万+用户:协作离线编辑”多人协作编辑文档,即使离线也能编辑,上线后自动合并所有人的修改。
你要解决什么
Section titled “你要解决什么”- 多人离线编辑+自动合并
- P2P直接同步选项
- 离线数据分析
给AI的Prompt
Section titled “给AI的Prompt”在V5基础上实现协作离线编辑:
多人协作:1. 文档共享:邀请其他用户协作编辑2. 在线时:WebSocket实时同步(字符级)3. 离线时:本地CRDT操作积累4. 上线后:交换积累的操作,CRDT自动合并5. 协作光标:显示其他在线用户的光标位置
P2P同步:1. WebRTC数据通道直连2. 局域网发现(mDNS)3. P2P优先,服务器兜底4. 适用于:同一办公室多人协作,网络不稳定但局域网可用5. 同步协议:交换Merkle树比较差异
离线分析:1. 本地统计:编辑字数、活跃时间、笔记数量2. IndexedDB存统计数据3. 联网时上报(可选)4. 隐私优先:分析在本地完成,不强制上报
存储优化:1. CRDT操作日志压缩(合并连续操作)2. 定期快照(snapshot),清理旧操作日志3. IndexedDB存储配额管理4. 超过限额时提示用户清理或同步
技术:Go微服务, PostgreSQL, WebRTC, CRDT, Merkle树, IndexedDB- 多人在线实时协作正常
- 离线编辑后上线自动合并
- 合并结果所有用户一致
- WebRTC P2P连接建立
- P2P同步数据正确
- 离线分析统计准确
- 操作日志压缩减少存储
- 快照+清理正常工作
你学到了什么
Section titled “你学到了什么”- 离线协作架构 → Module: 协作系统
- WebRTC P2P通信 → Module: P2P网络
- Merkle树数据比对 → Module: 数据结构
- 存储配额管理 → Module: 客户端优化