首页
/ QuillJS Delta格式设计原理深度解析

QuillJS Delta格式设计原理深度解析

2025-07-05 01:50:25作者:伍希望

前言:富文本编辑的格式困境

在富文本编辑器领域,长期以来缺乏一种标准化的方式来准确描述编辑器内容。大多数编辑器只是简单地输出HTML,将解析和解释的负担完全抛给使用者。这种状况导致了不同浏览器对相同HTML的解析差异,进而造成用户体验的不一致性。

QuillJS作为新一代富文本编辑器,通过创新的Delta格式解决了这一根本问题。Delta不仅是Quill内部使用的数据结构,更是一套完整的富文本描述规范。本文将深入剖析Delta格式的设计哲学和技术实现。

基础设计原则

从纯文本开始

Delta格式的设计始于最基本的纯文本表示。字符串(string)是表示纯文本最自然的方式,但当需要描述带格式的文本时,我们需要扩展这种表示方法。

Quill选择使用对象数组作为基础结构,这种设计具有以下优势:

  • 保持JSON兼容性,便于广泛工具链支持
  • 结构清晰,易于扩展
const content = [
  { text: 'Hello' },
  { text: 'World', bold: true }
];

结构化属性

为了保持代码整洁,Quill将所有格式属性统一放在attributes对象中:

const content = [
  { text: 'Hello' },
  { text: 'World', attributes: { bold: true } }
];

这种结构分离了文本内容与格式属性,使数据结构更加清晰。

核心约束条件

紧凑性(Compactness)

Delta格式强制要求表示必须尽可能紧凑。例如:

// 非紧凑表示(无效)
const content = [
  { text: 'Hel' },
  { text: 'lo' },
  { text: 'World', attributes: { bold: true } }
];

// 紧凑表示(有效)
const content = [
  { text: 'Hello' },
  { text: 'World', attributes: { bold: true } }
];

这种约束确保了Delta表示的唯一性和最小化。

规范化(Canonical)

Delta格式要求必须规范化,这意味着:

  • 相等的Delta必须表示相同的内容
  • 相同内容必须只有一种Delta表示方式
  • 键值对必须采用统一命名和值表示

例如,Quill默认使用:

  • 6位十六进制表示颜色(而非RGB)
  • \n作为唯一换行符表示
  • 明确的空间字符表示(如 

这种规范化使Delta处理更加可预测,降低了应用程序的复杂度。

行级格式处理

行格式(如对齐方式)会影响整行内容,这给Delta设计带来了特殊挑战。Quill的解决方案是:

  1. 将换行符作为行格式的载体
  2. 所有文档隐式添加一个换行符
  3. Delta必须始终以换行符结束
const content = [
  { text: "Hello" },
  { text: "\n", attributes: { align: "center" } },
  { text: "World" },
  { text: "\n", attributes: { align: "right" } } // 必须的结束换行符
];

这种设计确保了行格式的原子性和一致性。

嵌入式内容处理

Delta需要支持图片、视频等嵌入式内容。Quill采用统一的结构:

const content = [{
  insert: 'Hello'
}, {
  insert: 'World',
  attributes: { bold: true }
}, {
  insert: {
    image: 'https://example.com/image.png'
  },
  attributes: { width: '100' }
}];

关键设计点:

  • 使用insert作为通用字段名(替代最初的text
  • 嵌入式内容用对象表示,键为类型名
  • 嵌入式内容同样支持attributes

变更描述机制

Delta不仅可以描述文档状态,还能描述文档变更。这是通过操作(Operations)实现的。

操作类型

  1. 插入(insert):添加新内容
  2. 删除(delete):移除内容
  3. 保留(retain):保持内容不变
  4. 格式化(format):修改内容格式

关键设计演进

最初采用索引+长度的方式描述变更:

const delta = [{
  delete: {
    index: 4,
    length: 1
  }
}];

但这种方式存在重叠范围等问题。最终演变为更优雅的retain方案:

const change = [
  { retain: 5, attributes: { bold: true } },
  { insert: ' ' }
]

这种设计:

  • 消除了索引管理复杂性
  • 自动防止重叠范围
  • 更符合操作语义

最终Delta结构

考虑到JavaScript数组子类化的限制,最终Delta采用对象包装:

const delta = {
  ops: [{
    insert: 'Hello'
  }, {
    insert: 'World',
    attributes: { bold: true }
  }]
};

这种结构既保持了JSON兼容性,又为Delta类的方法扩展提供了基础。

总结

Quill的Delta格式通过精心设计,解决了富文本编辑领域的核心问题:

  1. 规范化表示确保一致性
  2. 紧凑性优化存储和传输
  3. 操作语义清晰描述变更
  4. 灵活支持各种内容类型

这些设计决策使Delta成为功能强大且易于使用的富文本描述规范,为Quill编辑器的强大功能奠定了基础。