Published on

Delta 格式的本质详解

Authors

Delta 格式的本质是什么?

🧠 Delta 格式的本质详解


🌱 1. Delta 格式的起源与设计理念

Delta 格式由 Quill.js 编辑器引入,旨在以 面向操作 的方式描述富文本内容和其变更。 其核心目标是:

  • 📝 简洁地描述内容和格式:避免复杂的 DOM 操作和数据结构。
  • 🔄 高效地同步和回放变更:便于协作编辑、撤销/重做、历史记录管理。
  • 📡 便于网络同步:以最小的变更单位进行数据传输,适用于 CRDT 和 OT 算法。

🔍 本质:Delta 是一种面向操作的文档变更描述格式

Delta ≠ 文本内容,它并不直接描述完整的文本,而是以 操作序列 描述文本内容及其变更。 每次编辑(插入、删除、样式变更)都会生成新的 Delta 操作,描述 从旧内容到新内容 的差异。


📑 2. Delta 格式的数据结构

Delta 的核心数据结构是一个有序的操作数组(Operations Array),每个操作由以下三个核心类型之一组成:

操作类型作用关键字段示例
insert在指定位置插入文本或嵌入对象insertattributes{"insert": "Hello"}
delete删除指定长度的文本delete{"delete": 5}
retain跳过指定长度的文本(通常用于修改样式)retainattributes{"retain": 6, "attributes": {"bold": true}}

🔧 2.1 操作格式

每个操作是一个 JSON 对象,具有以下字段:

字段类型含义示例
insertstring/object插入的文本或嵌入内容(如图片、视频)。{ "insert": "Hello" }
deletenumber删除指定长度的字符。{ "delete": 5 }
retainnumber跳过指定长度的字符(通常用于样式变更)。{ "retain": 6 }
attributesobject可选,描述文本或嵌入内容的样式和属性。{ "bold": true }

📜 2.2 示例:构建文本

我们通过 Delta 构建以下富文本: 文本内容Hello World 样式Hello 为普通文本,World 加粗。

Delta 表示

[
  { "insert": "Hello " },
  { "insert": "World", "attributes": { "bold": true } }
]

解读

  • 插入 Hello (无样式)。
  • 插入 World 并加粗。

等效文本: Hello **World**


🔄 3. Delta 格式的工作机制

🎯 3.1 增量变更描述

Delta 记录的并非完整的文档状态,而是通过操作数组描述从 上一个状态到当前状态变更过程

例如,文本从 Hello World 修改为 Hello Flutter

原始文本:

[{ "insert": "Hello World" }]

修改过程:

  1. 删除 World
[{ "retain": 6 }, { "delete": 5 }]
  1. 插入 Flutter
[{ "retain": 6 }, { "insert": "Flutter" }]

合并后的完整 Delta:

[
  { "retain": 6 },
  { "delete": 5 },
  { "insert": "Flutter" }
]

最终内容: Hello Flutter


🔍 3.2 样式变更

假设我们要将 Flutter 设置为加粗和红色:

[
  { "retain": 6 },
  { "retain": 7, "attributes": { "bold": true, "color": "red" } }
]

解释:

  • retain 6:跳过前 6 个字符(Hello )。
  • retain 7 + attributes:为接下来的 7 个字符(Flutter)加粗并设为红色。

🔄 3.3 合并与压缩

Delta 支持相同类型的连续操作进行合并,减少数据膨胀。

🆘 未优化:

[
  { "insert": "Hello " },
  { "insert": "World" }
]

优化后:

[
  { "insert": "Hello World" }
]

这种合并通常由 Delta.compose() 自动完成。


🌐 4. Delta 格式的优势

优势🚨 解释
🛠️ 结构简单基于数组和 JSON 格式,易于理解和实现。
🔄 便于协作编辑支持 CRDT 和 OT 算法,实现多人协作。
🚀 网络高效只需传输增量变更,而非整个文档内容。
🧠 撤销/重做简单每个 Delta 操作自带可逆性,撤销和重做基于操作序列。
🔍 格式与内容解耦内容由 insert 提供,样式由 attributes 管理,互不干扰。

⚠️ 5. Delta 格式的局限性

尽管 Delta 格式具备诸多优点,但在以下场景中可能存在性能和复杂性问题:

📈 5.1 数据膨胀

  • 频繁编辑、撤销和样式变更会导致操作数组无限增长。
  • 解决方案:定期合并相同类型的操作,使用 Delta.transform()Delta.compose()

⚠️ 5.2 嵌套结构支持有限

  • Delta 是线性数据结构,无法直接表达复杂的嵌套关系(如表格、树状列表)。
  • 解决方案:AppFlowy 结合 Node Tree 实现层次结构。

🔄 5.3 大文本性能

  • 处理大型文档时,Delta 的性能可能下降,尤其是基于光标的样式修改。
  • 解决方案:引入分块存储和懒加载策略。

🔧 6. Delta 的关键 API 方法

🛠️ 6.1 compose():合并两个 Delta

Delta.compose() 将当前 Delta 与另一个 Delta 合并,生成新版本。

示例:

const delta1 = new Delta().insert('Hello');
const delta2 = new Delta().insert(' World');
const merged = delta1.compose(delta2);
console.log(merged);
// => [{ insert: "Hello World" }]

🛠️ 6.2 transform():变换 Delta

在协作编辑中,Delta.transform() 用于在并发操作下调整光标位置和变更。

示例:

const deltaA = new Delta().insert('ABC');
const deltaB = new Delta().insert('D');
const transformed = deltaA.transform(deltaB, true);
console.log(transformed);

🛠️ 6.3 invert():生成撤销 Delta

Delta.invert() 会生成一个逆操作,用于实现撤销功能。

示例:

const original = new Delta().insert('Hello');
const change = new Delta().insert(' World');
const inverted = change.invert(original);
console.log(inverted);
// => [{ delete: 6 }]

🤖 7. AppFlowy 中 Delta 的应用

🔍 7.1 结合 Node Tree

  • Node Tree 描述文档层次结构。
  • Delta 专注于 TextNode 中的文本内容。

示例:

{
  "type": "text",
  "attributes": { "subtype": "heading", "heading": "h1" },
  "delta": [
    { "insert": "🌟 Welcome to AppFlowy!" }
  ]
}

解释

  • Node Tree 决定该节点为 h1 标题。
  • Delta 描述该标题内容。

🔍 7.2 事务管理(Transaction)

在 AppFlowy 中,每次编辑操作都会触发一个 Transaction,其内部包含 Delta

示例:按下 Enter 键

{
  "operations": [
    { "op": "insert", "path": [1], "nodes": [{ "type": "text", "delta": [{ "insert": "\n" }] }] },
    { "op": "update_text", "path": [0], "delta": [{ "retain": 5 }, { "insert": "\n" }] }
  ]
}
  • 插入新行节点。
  • 更新前一节点的文本内容。

🚀 8. Delta 的未来

随着协作编辑和富文本编辑需求的增长,Delta 格式仍具备强大的生命力和扩展性。 未来可能的改进方向:

  • 🌐 支持更复杂的嵌套结构:结合 JSON Schema。
  • ⚙️ 性能优化:更智能的合并与清理机制。
  • 🤝 增强协作能力:融合 CRDT、OT 和区块链技术。

🎯 9. 结论

  • Delta 是一种基于操作的变更描述格式,核心围绕 insertdeleteretain 三种操作展开。
  • 它更像是 文档的变更历史记录,而非最终内容的直接表达。
  • AppFlowy 巧妙地融合了 DeltaNode Tree,实现了灵活的富文本编辑功能。

🌟 Delta 格式的本质: 以最小的操作描述文档内容及其变更历史,为富文本编辑和协作提供高效、灵活和易扩展的基础。 🚀