Skip to content
目录

协同编辑与一致性策略

1. 协同层定位

Tiptap 协同扩展(@tiptap/extension-collaboration)本质是把 Yjs / y-prosemirror 能力接入到 Tiptap 的扩展体系中,而不是重写一套 CRDT。

核心文件:packages/extension-collaboration/src/collaboration.ts

2. 插件组合

协同扩展主要注入三类插件:

  • ySyncPlugin:文档同步。
  • yUndoPlugin:协同历史(区别于本地 history)。
  • 可选 filterInvalidContent 插件:内容校验开关时拦截非法同步数据。

3. 为什么与 undoRedo 不兼容

扩展在 onCreate 明确警告:

  • 协同场景已有 Yjs UndoManager。
  • 再叠加 @tiptap/extension-undo-redo 会出现双历史语义冲突。

这是典型“同一职责只能有一个真相源(source of truth)”。

源码片段(packages/extension-collaboration/src/collaboration.ts):

ts
if (this.editor.extensionManager.extensions.find(extension => extension.name === 'undoRedo')) {
  console.warn(
    '"@tiptap/extension-collaboration" comes with its own history support and is not compatible with "@tiptap/extension-undo-redo".',
  )
}

4. 命令层的协同适配

协同扩展覆写 undo/redo 命令时,会先:

  • tr.setMeta('preventDispatch', true) 避免走默认 dispatch 路径。
  • 检查 UndoManager 的栈是否为空。
  • 真正执行 undo(state) / redo(state)

这保证了命令 API 与协同事务模型一致,不会重复 dispatch。

5. 事务位置映射的重写

onBeforeCreate 中,协同扩展会替换 editor 的位置映射工具:

  • editor.utils.getUpdatedPosition
  • editor.utils.createMappablePosition

目的是让位置映射考虑协同事务语义,减少远端插入后的光标/锚点错位。

6. 内容安全阀(内容检查开启时)

enableContentCheck 开启:

  • 在 Yjs beforeTransaction 阶段把 fragment 转 JSON 并用 schema check() 校验。
  • 非法内容触发 contentError,并支持 disableCollaboration
  • 可销毁 Yjs doc 防止继续同步污染。

这在“弱信任协同输入”场景很关键。

7. 真实问题与修复证据

  • Issue: #1973(早期协同加载异常)
  • Changelog 中多次出现 collaboration/caret 稳定性修复
  • 代码内还保留对 y-prosemirror 既有问题的兼容注释(issues/102, 114)

可总结为:协同层不是一次性完成,而是持续对第三方库行为做适配收敛。

8. 如何讲清“协同一致性”

建议结构:

  1. 一致性模型由 Yjs 保证,Tiptap 负责接入层语义。
  2. 命令与事务元数据(preventDispatch)避免本地链路冲突。
  3. 内容校验 + disable 开关防止无效状态扩散。
  4. 插件顺序、历史系统、位置映射是协同场景三大高风险点。

MIT License.