代码语言

知识点思维导图

17 个知识节点

富文本编辑器(08) - Yjs 协同编辑

读完后,你应能完成以下任务:

  • 绘制“富文本编辑器(08) - Yjs 协同编辑 / 前言”的关键对象与数据流,解释“在多人协作的编辑场景中,如何保证文档的一致性是一个复杂的技术问题。”,并用源码位置、日志或 Trace 标注证据。
  • 为“富文本编辑器(08) - Yjs 协同编辑 / OT 算法”设计正常与异常输入,验证“用户 A 发送操作: insert("x", 1) -> 用户 B 发送操作: insert("y", 2)”,输出首个偏差位置与回归测试结果。
  • 实现“富文本编辑器(08) - Yjs 协同编辑 / 什么是 OT 算法”的最小代码或配置,检验“OT(Operational Transformation)是一种用于协同编辑的算法,它通过转换操作来解决冲突,确保所有用户看到相同的最终结果。”,输出命令、结果与 Diff,并说明不适用边界。

一、前言

在多人协作的编辑场景中,如何保证文档的一致性是一个复杂的技术问题。当多个用户同时编辑同一个文档时,需要解决冲突、保持同步,并确保最终结果的一致性。目前主要有两种算法来解决这个问题:OT(Operational Transformation)CRDT(Conflict-free Replicated Data Types)

二、OT 算法

2.1 什么是 OT 算法

OT(Operational Transformation)是一种用于协同编辑的算法,它通过转换操作来解决冲突,确保所有用户看到相同的最终结果。

2.2 工作原理

假设我们有一个文档,内容是 "abc",两个用户同时编辑:

初始状态: "abc"

用户 A 的操作: 在位置 1 插入 "x""axbc" 用户 B 的操作: 在位置 2 插入 "y""abyc"

如果没有 OT 算法,直接应用操作会导致冲突:

  • 用户 A 看到:"axbc"
  • 用户 B 看到:"abyc"

2.3 OT 算法解决冲突

OT 算法的核心思想是转换操作,让操作能够适应其他用户的操作:

  1. 用户 A 发送操作: insert("x", 1)
  2. 用户 B 发送操作: insert("y", 2)
  3. 服务器转换操作:
    • 用户 A 收到:insert("x", 1) + 转换后的 insert("y", 3)"axbyc"
    • 用户 B 收到:insert("y", 2) + 转换后的 insert("x", 1)"axbyc"

最终结果: 两个用户都看到 "axbyc"

2.4 OT 算法的特点

  • 实时性好:操作立即生效
  • 冲突解决:通过转换操作解决冲突
  • 复杂度高:转换规则复杂,难以实现
  • 中心化:需要中央服务器协调

可视化的展示 OT 算法的执行和流转过程 https://operational-transformation.github.io/index.html

三、CRDT 算法

3.1 什么是 CRDT 算法

CRDT(Conflict-free Replicated Data Types)是一种无冲突的复制数据类型,它通过设计数据结构本身来避免冲突,而不需要转换操作。

3.2 工作原理

同样以文档 "abc" 为例,两个用户同时编辑:

初始状态: "abc"

用户 A 的操作: 在位置 1 插入 "x" 用户 B 的操作: 在位置 2 插入 "y"

3.3 CRDT 算法解决冲突

CRDT 通过给每个字符分配唯一标识符来避免冲突。下面是简单方便理解的例子,具体 CRDT 更复杂:

初始状态: "abc"

  • "a" 的标识符:(0, 'A')
  • "b" 的标识符:(1, 'A')
  • "c" 的标识符:(2, 'A')

用户 A 在位置 1 插入 "x"

  • 新字符 "x" 的标识符:(0.5, 'A')(在 0 和 1 之间)
  • 结果:"axbc"

用户 B 在位置 2 插入 "y"

  • 新字符 "y" 的标识符:(1.5, 'B')(在 1 和 2 之间)
  • 结果:"abyc"

合并时按标识符排序:

  • (0, 'A')"a"
  • (0.5, 'A')"x"
  • (1, 'A')"b"
  • (1.5, 'B')"y"
  • (2, 'A')"c"

最终结果: "axbyc"

3.4 CRDT 算法的特点

  • 去中心化:不需要中央服务器
  • 简单可靠:算法相对简单,不容易出错
  • 最终一致性:保证最终结果一致
  • 存储开销:需要存储额外的元数据
  • 延迟较高:需要等待所有操作到达

四、Yjs 中的实现

4.1 Yjs 简介

Yjs 是一个基于 CRDT 的协同编辑库

4.2 在 TipTap 中集成 Yjs

Yjs 点过来跳转到了Tiptap,也就是这里: https://tiptap.dev/docs/collaboration/getting-started/install

当然也可以直接看这个Demo:https://tiptap.dev/docs/examples/advanced/collaborative-editing,这是官方提供的商业服务 tiptap cloud

注意用 tiptap cloud 7天没有使用需要在 https://cloud.tiptap.dev/v1/apps/settings 恢复应用,注意只有 30 天试用期

也有免费的,应该是大部分的人的选择,也就是 Hocuspocus

启动后端服务

下面是最简单的,但是我们往往需要自己部署后端服务,可以参考 https://tiptap.dev/docs/hocuspocus/server/examples

npx @hocuspocus/cli

前端 tiptap 接入文档 https://tiptap.dev/docs/hocuspocus/provider/examples#tiptap

'use client'
import './styles.scss'

import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import { EditorContent, useEditor } from '@tiptap/react'

import Collaboration from '@tiptap/extension-collaboration'
import * as Y from 'yjs'

// Importing the provider and useEffect
import { useEffect } from 'react'
import { HocuspocusProvider } from '@hocuspocus/provider'

const ydoc = new Y.Doc()

export default function Page() {
  const editor = useEditor({
    immediatelyRender: false,
    extensions: [
      Document,
      Paragraph,
      Text,
      Collaboration.configure({
        document: ydoc
      })
    ],
    content: `
      <p>
        This is a radically reduced version of Tiptap. It has support for a document, with paragraphs and text. That’s it. It’s probably too much for real minimalists though.
      </p>
      <p>
        The paragraph extension is not really required, but you need at least one node. Sure, that node can be something different.
      </p>
    `
  })

  // Connect to your Collaboration server
  useEffect(() => {
    const provider = new HocuspocusProvider({
      url: 'ws://127.0.0.1:1234',
      name: 'example-document',
      document: ydoc
    })
  }, [])

  return <EditorContent editor={editor} />
}

4.3 实时同步流程

  1. 用户输入 → TipTap 编辑器
  2. 生成操作 → Yjs 处理
  3. 网络传输 → WebSocket/WebRTC
  4. 接收操作 → 其他客户端
  5. 应用操作 → 更新编辑器

五、总结

  • 前言:在多人协作的编辑场景中,如何保证文档的一致性是一个复杂的技术问题。
  • CRDT 算法:CRDT(Conflict-free Replicated Data Types)是一种无冲突的复制数据类型,它通过设计数据结构本身来避免冲突,而不需要转换操作。
  • Yjs 中的实现:用户输入 → TipTap 编辑器 -> 生成操作 → Yjs 处理 -> 网络传输 → WebSocket/WebRTC -> 接收操作 → 其他客户端
  • 什么是 OT 算法:OT(Operational Transformation)是一种用于协同编辑的算法,它通过转换操作来解决冲突,确保所有用户看到相同的最终结果。
  • OT 算法的特点:✅ 冲突解决:通过转换操作解决冲突
  • CRDT 算法解决冲突:CRDT 通过给每个字符分配唯一标识符来避免冲突。

学完自测

选择所有正确答案;提交后逐项核对判断依据。

1在“Yjs 协同编辑”中,需要同时满足“前言”与“什么是 OT 算法”。给定正文约束“当多个用户同时编辑同一个文档时,需要解决冲突、保持同步,并确保最终结果的一致性。”,哪些判断保持了原有处理机制?多选
2“Yjs 协同编辑”出现偏差:“在“Yjs 协同编辑 / 工作原理”中,即使不满足“同样以文档 "abc" 为例,两个用户同时编辑”,结果与副作用仍会保持不变。”已成为实际行为。围绕“工作原理”与“OT 算法解决冲突”,哪些判断能定位被改变的职责或边界?多选
3评审“Yjs 协同编辑”方案时,验收条件包含“冲突解决:通过转换操作解决冲突”。关于“OT 算法的特点”与“什么是 CRDT 算法”的哪些决策符合正文机制?多选