实现石墨文档级协同编辑需前后端协同:前端监听selectionchange轻量广播光标位置,将操作拆为可逆原子指令;服务端仅校验广播不解析内容;前端须维护操作队列、版本号及独立光标渲染层。

WebSocket 本身不处理光标或操作逻辑,它只是管道。真正实现石墨文档级的协同编辑,关键在于“前端怎么发、服务端怎么转、前端怎么收并安全应用”——三者配合,缺一不可。
光标同步:只传位置,不传状态
光标是瞬时状态,不能靠定时轮询或全量快照。正确做法是监听浏览器原生事件,轻量广播:
- 监听 selectionchange 事件,在用户点击、拖选、键盘移动时触发
- 提取当前光标位置(
getRangeAt(0).getBoundingClientRect()或更稳妥的编辑器坐标映射,如 CodeMirror 的coordsChar()) - 封装为最小消息:
{ type: 'cursor', userId: 'u123', name: '李工', color: '#e74c3c', line: 5, ch: 12 }(line/ch 更可靠,避免像素偏移) - 通过 WebSocket 每秒最多发 1–3 次(防抖),不带 DOM 引用或 selection 对象
操作同步:必须拆成可逆原子操作
绝不能直接传整个 HTML 或 innerText。否则两人同时改同一行,后到者覆盖前者的修改——这不是同步,是竞态擦除。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 把每次输入/删除转为结构化操作,例如:
{ type: 'insert', index: 142, text: '协作', clientId: 'u123', version: 47 }{ type: 'delete', index: 88, len: 2, clientId: 'u456', version: 48 } - 前端本地先执行,并暂存未确认的操作(pending queue)
- 服务端不做内容合并,只按房间广播;但需透传 clientId 和 version,供前端做 OT/CRDT 计算
- 收到他人操作后,不直接 apply,而是交由 OT 变换函数调整 index 偏移,再执行
服务端只做“邮局”,不越界处理
Java(Spring WebSocket)或 Node.js(ws 库)服务端职责非常明确:
- 连接时绑定 roomId 和 userId(从 token 或 query 解析)
- 收到消息后,校验 type 字段合法性(如只允许 insert/delete/cursor/clear),过滤非法字段
- 向同房间其他客户端广播原始 JSON,**不解析 content、不计算 diff、不渲染预览**
- 对 clear 等敏感操作,可加简单房主校验(
if (msg.type === 'clear' && room.ownerId !== msg.userId) return;)
前端必须自己维护三样东西
没有这些,光靠 WebSocket 连上就崩:
- 本地操作队列:用户敲字时先记下,等服务端 ack 后才标记为 confirmed,未 ack 的操作要参与 OT 变换
- 文档版本号(或操作序列号):每次成功 apply 操作后自增,用于判断消息是否过期或乱序
- 光标渲染层独立于编辑器:用绝对定位 div 叠在编辑器上方,根据收到的 cursor 消息动态更新位置和颜色,不干扰编辑逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










