# Design: postmessage-duplex 通信层重构 ## Context 当前系统使用原生 `postMessage` API 实现 SW 与应用层通信: - 应用层 → SW:直接 `controller.postMessage()` - SW → 应用层:`broadcastToClients()` 广播所有窗口 这种模式在多标签页场景下存在问题: 1. 多个页面同时提交相同任务,SW 的去重检查存在时间窗口 2. 广播消息所有页面都会收到,无法区分请求来源 3. 请求-响应模式需要手动用 RxJS 实现 ## Goals / Non-Goals ### Goals - 使用 postmessage-duplex 实现双工通信 - 解决多标签页任务重复问题 - 简化请求-响应模式的实现 - 保持向后兼容(渐进式迁移) ### Non-Goals - 不改变现有业务逻辑 - 不改变 IndexedDB 存储结构 - 不改变任务执行流程 ## Decisions ### 1. 通信架构 ``` ┌─────────────────┐ ┌──────────────────────┐ ┌─────────────────┐ │ Tab 1 (Page) │ │ Service Worker │ │ Tab 2 (Page) │ ├─────────────────┤ ├──────────────────────┤ ├─────────────────┤ │ ServiceWorker │◄──────►│ Per-client channels │◄──────►│ ServiceWorker │ │ Channel │ RPC │ Map│ RPC │ Channel │ └─────────────────┘ └──────────────────────┘ └─────────────────┘ ``` - 每个页面创建独立的 `ServiceWorkerChannel` - SW 端维护 `Map` 管理所有连接 - 请求-响应使用 `call` 方法,事件通知使用 `subscribe` ### 2. 消息类型划分 | 操作 | 当前方式 | 改造后方式 | |------|---------|-----------| | 任务创建 | postMessage + 监听 | `call('task:create', params)` → `{taskId, status}` | | 任务取消 | postMessage | `call('task:cancel', {taskId})` → `{success}` | | 任务重试 | postMessage | `call('task:retry', {taskId})` → `{success}` | | 获取任务列表 | postMessage + timeout | `call('task:list')` → `{tasks}` | | 任务状态变更 | broadcast | `subscribe('task:status')` | | 任务完成 | broadcast | `subscribe('task:completed')` | | Chat 流式 | broadcast | `subscribe('chat:chunk')` | ### 3. 任务创建流程改造 **当前流程**: ``` 1. 应用层生成 taskId 2. 本地创建任务(乐观更新) 3. postMessage 到 SW 4. SW 检查重复,可能拒绝 5. 广播 TASK_CREATED ``` **改造后流程**: ``` 1. 应用层调用 channel.call('task:create', params) 2. SW 检查重复 - 已存在:返回 {success: false, existingTaskId} - 新建:创建任务,返回 {success: true, task} 3. 应用层根据响应更新本地状态 4. SW 广播 task:created 给其他客户端(排除发起者) ``` ### 4. 多页面同步 - SW 维护所有通道引用 - 当任务状态变更时,广播给所有订阅该事件的通道 - 每个通道独立订阅,避免重复处理 ### 5. Service Worker 端集成 由于 SW 不支持直接 import npm 模块,有两种方案: **方案 A(推荐)**:打包时内联 - 使用 Vite 的 `?inline` 或自定义打包将库代码内联到 SW **方案 B**:复制核心代码 - 将 postmessage-duplex 的 SW 相关代码复制到项目中 选择 **方案 A**,在 SW 构建配置中处理。 ## Risks / Trade-offs | Risk | Mitigation | |------|------------| | SW 端集成复杂 | 使用打包工具处理,文档中有示例 | | 迁移风险 | 渐进式迁移,保留旧消息类型兼容 | | 库稳定性 | 库较新(v1.0.0),需关注 issues | ## Migration Plan ### Phase 1:基础设施 1. 安装 postmessage-duplex 依赖 2. 配置 SW 打包支持 3. 创建新的通信服务(与现有并存) ### Phase 2:核心功能迁移 1. 迁移任务创建为 call 模式 2. 迁移任务状态为 subscribe 模式 3. 迁移 Chat 流式为 subscribe 模式 ### Phase 3:清理 1. 移除旧的 RxJS 订阅逻辑 2. 移除兼容代码 3. 更新文档 ## Open Questions 1. SW 打包方式的具体实现细节? - 需要测试 Vite 对 SW 入口的打包行为 2. 是否需要支持消息压缩? - 当前任务数据量不大,暂不需要