- Published on
什么是 Delta 数据结构
- Authors
- Name
- Alex
- @adams6688
Delta 的本质
Delta 是 一种面向操作(Operational Transformation, OT)的变更数据结构,用于描述 富文本的增量变更。
它并不是传统的存储结构(如数组、树、哈希表),而是一个有序的变更操作序列(Operations Array)。
在富文本编辑器 quill 和 slatejs 中,Delta 是一种常见的数据结构,用于描述文本的增量变更。Delta 本质上是一个数组,数组中的每个元素都是一个操作对象,用于描述文本的增量变更。
但是,Delta 并不仅仅是一个简单的数组,它还包含了一些元数据,比如文本的格式信息、样式信息等。这些元数据可以帮助编辑器正确地渲染文本,并保持文本的格式和样式。
Delta 的核心特点
基于操作的线性结构:使用 insert、delete、retain 记录文本和样式变更。 增量存储:不是存储一个完整的文档,而是存储文档变更的历史。
可以进行如下定义
interface Operation {
insert?: string | object;
delete?: number;
retain?: number;
attributes?: Record<string, any>;
}
type Delta = Operation[];
📜 Delta 的数据结构解析
🧐 1. Delta 的本质
❓ Delta 是什么?
Delta
是 一种面向操作(Operational Transformation, OT)的变更数据结构,用于描述 富文本的增量变更。 它并不是传统的存储结构(如数组、树、哈希表),而是一个有序的变更操作序列(Operations Array)。
🌱 1.1 Delta 的核心特点
- 基于操作的线性结构:使用
insert
、delete
、retain
记录文本和样式变更。 - 增量存储:不存储完整文档,而仅记录从旧状态到新状态的 变更过程。
- 支持撤销/重做:每个操作都可以回放或反向应用,实现 undo/redo。
- 适用于协作编辑:与 CRDT、OT 算法兼容,便于多人编辑同步。
🌍 1.2 Delta 与传统数据结构的区别
特性 | Delta | 文本字符串 | DOM 结构 |
---|---|---|---|
数据存储 | 增量存储,仅记录变更 | 存储完整内容 | 结构化存储,含层次关系 |
操作方式 | 基于 insert /delete /retain | 直接修改字符串 | 通过 HTML 结构管理 |
撤销机制 | 通过 invert() 反转操作 | 需要手动维护版本 | DOM Diff 计算变化 |
协作能力 | 适用于 CRDT、OT 算法 | 不支持协作 | 依赖 WebSocket 或 Diff 算法 |
🏗️ 2. Delta 的数据结构
📂 2.1 结构定义
Delta 是由多个 操作对象(Operation) 组成的 有序数组(Operations Array),其中每个操作都表示 对文档的一个变更。
🔧 核心数据结构
interface Operation {
insert?: string | object;
delete?: number;
retain?: number;
attributes?: Record<string, any>;
}
type Delta = Operation[];
🎯 操作类型
操作类型 | 作用 | 示例 |
---|---|---|
insert | 在指定位置插入文本或对象 | { "insert": "Hello" } |
delete | 删除指定长度的文本 | { "delete": 5 } |
retain | 跳过指定长度的文本(通常用于修改样式) | { "retain": 6, "attributes": { "bold": true } } |
📜 2.2 Delta 操作示例
🌟 示例 1:插入文本
[
{ "insert": "Hello " },
{ "insert": "World", "attributes": { "bold": true } }
]
解读:
- 插入
"Hello "
普通文本。 - 插入
"World"
并加粗。
📝 示例 2:删除文本
假设当前文档是 "Hello World"
,我们想删除 "World"
:
[
{ "retain": 6 },
{ "delete": 5 }
]
解读:
retain 6
:跳过"Hello "
(不变)。delete 5
:删除"World"
。
🎨 示例 3:修改样式
将 "Hello"
设为斜体:
[
{ "retain": 5, "attributes": { "italic": true } }
]
解读:
retain 5
:跳过"Hello"
。attributes
:应用italic
样式。
📌 示例 4:综合操作
假设我们从 "Hello World"
修改为:
- 删除
"World"
- 插入
"Flutter"
- 加粗 Flutter
[
{ "retain": 6 },
{ "delete": 5 },
{ "insert": "Flutter", "attributes": { "bold": true } }
]
最终结果:Hello **Flutter**
🔄 3. Delta 的变更合并(Compose)
Delta.compose()
用于合并两个 Delta
操作,减少冗余数据,提高存储效率。
🆘 未优化(两次插入)
[
{ "insert": "Hello" },
{ "insert": " World" }
]
✅ 优化后(合并)
[
{ "insert": "Hello World" }
]
🔀 4. Delta 的协作能力
在多人协作编辑中,Delta
可以通过 transform()
方法进行合并,避免冲突。
示例:两人同时编辑
- 用户 A:在
"Hello"
之后插入"Flutter"
。 - 用户 B:将
"Hello"
加粗。
用户 A
[
{ "retain": 5 },
{ "insert": " Flutter" }
]
用户 B
[
{ "retain": 5, "attributes": { "bold": true } }
]
合并后的 Delta
[
{ "retain": 5, "attributes": { "bold": true } },
{ "insert": " Flutter" }
]
最终效果:**Hello** Flutter
🏆 5. Delta 的优势与局限性
✅ 5.1 优势
特性 | 优势 |
---|---|
可增量存储 | 只存储变更,不存储完整文档 |
撤销/重做便捷 | 通过 invert() 生成反向操作 |
协作编辑友好 | 支持 CRDT / OT 变更合并 |
格式独立 | 文本与样式分离,适用于多种富文本编辑器 |
❌ 5.2 局限性
问题 | 解决方案 |
---|---|
数据膨胀(增量存储过多) | 使用 Delta.compose() 进行合并 |
无法表示复杂结构(如表格) | 结合 Node Tree 存储嵌套结构 |
需要额外处理光标位置 | 结合 Selection 结构进行同步 |
🎯 6. 结论:Delta 的本质
🔑 Delta 不是简单的数据存储结构,而是一个基于操作序列的富文本变更描述格式。 它通过 增量存储、撤销/重做支持、协作编辑优化,在富文本编辑器(如 Quill.js、AppFlowy)中具有广泛应用。
📌 关键总结
- Delta 是有序的操作列表,描述文档的变更过程,而非最终内容。
- 支持撤销/重做,能通过
invert()
还原操作。 - 适用于多人协作编辑,能通过
transform()
解决冲突。 - 可组合(compose)和优化,减少数据冗余,提升存储和渲染性能。
📌 核心洞察:
“Delta 不是文档,而是文档的变更历史。” 🚀