实时协作-yjs基本理解4-Ytext

📅
1 分钟阅读
·

Yjs 中的 Y.Text 是一种 CRDT(Conflict-free replicated data types),用于处理分布式环境中的实时协作文本编辑。Y.Text 允许多个用户同时在文本任意位置插入和删除,无需处理冲突或数据不一致。

Y.Text 的实现涉及两个概念:

  1. 操作的唯一标识符: 在 Yjs 中,每个插入或删除操作都由全局唯一标识符标识。该标识符由生成操作的用户唯一标识符和递增的逻辑时钟值组成,因此两个用户在同一位置操作时,操作仍可区分。
  2. 操作的部分排序: Yjs 使用唯一标识符为操作定义部分排序。若操作 A 的标识符小于操作 B 的标识符,则操作 A 在操作 B 之前。同一位置发生多个操作时,这个排序用于处理冲突,标识符较小的操作先执行。

插入操作为每个字符分配全局唯一标识符(ID)。ID 由生成操作的客户端唯一 ID 和该客户端递增的逻辑时钟组成。网络延迟存在时,不同客户端的操作仍可区分。

删除操作会记录每个字符的删除信息,并与插入操作一起传播到所有副本。副本收到删除操作后,从文档中删除对应字符。

Y.Text 没有明确的“更新”操作,而是通过删除和插入组合实现更新。例如,替换某个位置的字符时,先删除该字符,再插入新字符。

同一位置发生两个或更多操作时,Yjs 比较操作 ID 以确定顺序。ID 全局唯一且存在定义好的顺序,因此所有副本都会得到相同结果,不受接收操作顺序影响。

并发操作到达时,Yjs 依据上述规则确定顺序。只要所有副本都收到所有操作,它们最终会达到相同状态。

Yjs 还通过操作日志保存插入和删除操作。操作日志保存的内容可用于重建当前文档状态。

下面是用于说明这些概念的粗略实现:

class YText {
  constructor() {
    this.ops = []; // 操作日志
    this.text = []; // 文本的当前状态
    this.clientId = Math.random().toString(36).substring(2); // 随机生成客户端ID
    this.clock = 0; // 逻辑时钟
  }

  insert(index, char) {
    const id = { clientId: this.clientId, clock: this.clock++ };
    this.ops.push({ type: 'insert', id, index, char });
    this.text.splice(index, 0, { id, char });
  }

  delete(index) {
    const { id } = this.text[index];
    this.ops.push({ type: 'delete', id, index });
    this.text.splice(index, 1);
  }

  // 处理来自其他客户端的操作
  receive(operation) {
    if (operation.type === 'insert') {
      this.text.splice(operation.index, 0, { id: operation.id, char: operation.char });
    } else if (operation.type === 'delete') {
      const index = this.text.findIndex(c => compareIds(c.id, operation.id) === 0);
      if (index >= 0) {
        this.text.splice(index, 1);
      }
    }
    // 对文本进行重新排序
    this.text.sort((a, b) => compareIds(a.id, b.id));
  }
}

// ID比较函数
function compareIds(id1, id2) {
  if (id1.clientId < id2.clientId) {
    return -1;
  } else if (id1.clientId > id2.clientId) {
    return 1;
  } else {
    return id1.clock - id2.clock;
  }
}

示例假设操作按顺序接收,不处理网络延迟和乱序操作。实际的 Yjs 需要使用更多逻辑和数据结构处理这些情况,包括状态向量、删除集和操作转换。

这个示例未覆盖乱序操作、网络分区等问题;Yjs 的实际实现需要处理这些情况,仍需结合源码分析。


90 字 · 13 段落
ximing

Follow onGitHub

相关文章