quill.js 的文档结构 —— delta 的使用 | Delta Format

Deltas 是一种简单但富有表现力的格式,可用于描述 Quill 的内容和变化。该格式是 JSON 的严格子集,是可读的,并且很容易被机器解析。Deltas 可以描述任何 Quill 文档,包括所有文本和格式信息,与HTML相比,Deltas 的优势在于其简洁性和易读性。HTML作为一种标记语言,包含了大量的标签和属性,使得文档结构复杂而难以理解。而达尔塔则采用了JSON格式,使得文档的结构清晰明了,易于阅读和理解。

Delta由操作数组组成,用于描述对文档的更改。它们可以insertdeleteretain。注意,其操作不采用索引。它们总是描述当前索引的变化。使用 retains 可以"保留"或"跳过"文档的某些部分。

不要被它的名称 Delta 混淆 - Deltas 既表示文档,也表示对文档的更改。如果将 Deltas 视为从一个文档到另一个文档的指令,则 Deltas 表示文档的方式是从空文档开始表示指令。

Deltas 作为单独的独立库实现,允许在 Quill 之外使用。它是适用于OT(Operational Transform)的,可以在realtime、Google Docs 等应用程序中使用。有关 Delta 背后的更深入说明,请参阅Designing the Delta Format

注意: 不建议手动构造 Deltas,而是使用可链接的 insert()delete()) 和 retain() 方法来创建新的 Deltas。你可以使用 import() 从 Quill 访问 Delta。

快速示例

js 复制代码
// Document with text "Gandalf the Grey"
// with "Gandalf" bolded, and "Grey" in grey
const delta = new Delta([
  { insert: 'Gandalf', attributes: { bold: true } },
  { insert: ' the ' },
  { insert: 'Grey', attributes: { color: '#ccc' } }
]);

// Change intended to be applied to above:
// Keep the first 12 characters, insert a white 'White'
// and delete the next four characters ('Grey')
const death = new Delta().retain(12)
                         .insert('White', { color: '#fff' })
                         .delete(4);
// {
//   ops: [
//     { retain: 12 },
//     { insert: 'White', attributes: { color: '#fff' } },
//     { delete: 4 }
//   ]
// }

// Applying the above:
const restored = delta.compose(death);
// {
//   ops: [
//     { insert: 'Gandalf', attributes: { bold: true } },
//     { insert: ' the ' },
//     { insert: 'White', attributes: { color: '#fff' } }
//   ]
// }

本自述文件描述了 Deltas 的一般形式和 API 功能。有关 Quill 具体使用 Delta 的方式的其他信息,请参阅其自己的 Delta 文档。Delta 背后的动机和设计思维的演练是关于Delta 格式设计的。

此格式适用于操作转换,并定义了多个函数来支持此用例。

Operations

插入操作

插入操作定义了insert键。String 值表示插入文本。任何其他类型都表示插入嵌入(但是,为了相等,将只执行一个级别的对象比较)。

在文本和嵌入这两种情况下,都可以使用 Object 定义可选attributes键来描述附加格式信息。可以通过retain操作更改格式。

js 复制代码
// Insert a bolded "Text"
{ insert: "Text", attributes: { bold: true } }

// Insert a link
{ insert: "Google", attributes: { link: 'https://www.google.com' } }

// Insert an embed
{
  insert: { image: 'https://octodex.github.com/images/labtocat.png' },
  attributes: { alt: "Lab Octocat" }
}

// Insert another embed
{
  insert: { video: 'https://www.youtube.com/watch?v=dMH0bHeiRNg' },
  attributes: {
    width: 420,
    height: 315
  }
}

删除操作

删除操作定义了一个数字删除键,表示要delete的字符数。所有嵌入的长度均为 1。

js 复制代码
// Delete the next 10 characters
{ delete: 10 }

保留操作

retain 操作定义了一个 Number retain 键,表示要保留的字符数(其他库可能使用名称 keep 或 skip)。可以使用 Object 定义可选attributes键,以描述对字符范围的格式更改。attributes Object 中的 null 值表示删除该键。

注意:没有必要保留文档的最后字符,因为这是隐含的。

js 复制代码
// Keep the next 5 characters
{ retain: 5 }

// Keep and bold the next 5 characters
{ retain: 5, attributes: { bold: true } }

// Keep and unbold the next 5 characters
// More specifically, remove the bold key in the attributes Object
// in the next 5 characters
{ retain: 5, attributes: { bold: null } }

构造器

构造函数

创建新的 Delta 对象。

注意:使用 ops 或 delta 构造时,不执行有效性/健全性检查。新增量的内部操作数组也将从 ops 或 delta.ops 分配,而无需进行深拷贝。

Example

js 复制代码
const delta = new Delta([
  { insert: 'Hello World' },
  { insert: '!', attributes: { bold: true }}
]);

const packet = JSON.stringify(delta);

const other = new Delta(JSON.parse(packet));

const chained = new Delta().insert('Hello World').insert('!', { bold: true });

insert() 插入

追加插入操作。返回this值以获得可链接性。

Example

js 复制代码
delta.insert('Text', { bold: true, color: '#ccc' });
delta.insert({ image: 'https://octodex.github.com/images/labtocat.png' });

delete() 删除

追加删除操作。返回 this 以实现可链接性。

Example

go 复制代码
delta.delete(5);

retain() 保留

追加 retain 操作。返回this值以获得可链接性。

Example

css 复制代码
delta.retain(4).retain(5, { color: '#0c6' });

文档操作

concat()连接

返回一个新的 Delta,表示此文档与另一个文档 Delta 操作的串联。

js 复制代码
const a = new Delta().insert('Hello');
const b = new Delta().insert('!', { bold: true });


// {
//   ops: [
//     { insert: 'Hello' },
//     { insert: '!', attributes: { bold: true } }
//   ]
// }
const concat = a.concat(b);

diff() 比较差异

返回表示两个文档之间差异的 Delta。 (可选)接受发生更改的建议索引,通常表示更改之前的光标位置。

js 复制代码
const a = new Delta().insert('Hello');
const b = new Delta().insert('Hello!');

const diff = a.diff(b);  // { ops: [{ retain: 5 }, { insert: '!' }] }
                         // a.compose(diff) == b

eachLine() 迭代每行

迭代文档 Delta,使用 Delta 和表示线段的属性对象调用给定函数。

js 复制代码
const delta = new Delta().insert('Hello\n\n')
                         .insert('World')
                         .insert({ image: 'octocat.png' })
                         .insert('\n', { align: 'right' })
                         .insert('!');

delta.eachLine((line, attributes, i) => {
  console.log(line, attributes, i);
  // Can return false to exit loop early
});
// Should log:
// { ops: [{ insert: 'Hello' }] }, {}, 0
// { ops: [] }, {}, 1
// { ops: [{ insert: 'World' }, { insert: { image: 'octocat.png' } }] }, { align: 'right' }, 2
// { ops: [{ insert: '!' }] }, {}, 3

invert() 反转

返回一个反向增量,其与基本文档增量具有相反的效果。即 base.compose(delta).compose(inverted) === base

js 复制代码
const base = new Delta().insert('Hello\n')
                        .insert('World');
const delta = new Delta().retain(6, { bold: true }).insert('!').delete(5);

const inverted = delta.invert(base);  // { ops: [
                                      //   { retain: 6, attributes: { bold: null } },
                                      //   { insert: 'World' },
                                      //   { delete: 1 }
                                      // ]}
                                      // base.compose(delta).compose(inverted) === base

工具方法

delta本身格式就是以类数组的形式,那么他也就理所当然会支持数据

filter()

返回传递给定函数的操作数组

js 复制代码
const delta = new Delta().insert('Hello', { bold: true })
                         .insert({ image: 'https://octodex.github.com/images/labtocat.png' })
                         .insert('World!');

const text = delta
  .filter((op) => typeof op.insert === 'string')
  .map((op) => op.insert)
  .join('');

forEach()

迭代操作,为每个操作调用提供的函数

js 复制代码
delta.forEach((op) => {
  console.log(op);
});

length()

返回 Delta 的长度,它是其操作的长度之和。

js 复制代码
new Delta().insert('Hello').length();  // Returns 5

new Delta().insert('A').retain(2).delete(1).length(); // Returns 4

map()

返回一个新数组,其中包含对每个操作调用提供的函数的结果。

js 复制代码
const delta = new Delta().insert('Hello', { bold: true })
                         .insert({ image: 'https://octodex.github.com/images/labtocat.png' })
                         .insert('World!');

const text = delta
  .map((op) => {
    if (typeof op.insert === 'string') {
      return op.insert;
    } else {
      return '';
    }
  })
  .join('');

partition() 组合

创建一个由两个数组组成的新数组,返回的数据组中的第一个数组包含通过给定函数操作的,另一个则是失败的数组。

js 复制代码
const delta = new Delta().insert('Hello', { bold: true })
                         .insert({ image: 'https://octodex.github.com/images/labtocat.png' })
                         .insert('World!');

const results = delta.partition((op) => typeof op.insert === 'string');
const passed = results[0];  // [{ insert: 'Hello', attributes: { bold: true }},
                            //  { insert: 'World'}]
const failed = results[1];  // [{ insert: { image: 'https://octodex.github.com/images/labtocat.png' }}]

reduce()

将给定函数应用于累加器和每个操作以减少到单个值。

js 复制代码
const delta = new Delta().insert('Hello', { bold: true })
                         .insert({ image: 'https://octodex.github.com/images/labtocat.png' })
                         .insert('World!');

const length = delta.reduce((length, op) => (
  length + (op.insert.length || 1);
), 0);

slice()

js 复制代码
const delta = new Delta().insert('Hello', { bold: true }).insert(' World');

// {
//   ops: [
//     { insert: 'Hello', attributes: { bold: true } },
//     { insert: ' World' }
//   ]
// }
const copy = delta.slice();

// { ops: [{ insert: 'World' }] }
const world = delta.slice(6);

// { ops: [{ insert: ' ' }] }
const space = delta.slice(5, 6);

操作(operations)转换

compose() 撰写

返回一个 Delta,相当于应用自己的 Delta 的operations,然后再应用另一个 Delta。

js 复制代码
const a = new Delta().insert('abc');
const b = new Delta().retain(1).delete(1);

const composed = a.compose(b);  // composed == new Delta().insert('ac');

transform() 转换

将给定的Delta转换为自己的operations

js 复制代码
const a = new Delta().insert('a');
const b = new Delta().insert('b').retain(5).insert('c');

a.transform(b, true);  // new Delta().retain(1).insert('b').retain(5).insert('c');
a.transform(b, false); // new Delta().insert('b').retain(6).insert('c');

transformPosition() 变换位置

根据 Delta 变换索引。对于表示光标/选择位置很有用。

js 复制代码
const delta = new Delta().retain(5).insert('a');
delta.transformPosition(4); // 4
delta.transformPosition(5); // 6

参考

相关推荐
AI_零食7 小时前
番茄钟鸿蒙PC Electron框架完成:状态机、定时器管理与专注力工具设计
前端·javascript·华为·electron·开源·鸿蒙·鸿蒙系统
提子拌饭1337 小时前
逛三园游戏——基于鸿蒙PC Electron框架实现
前端·javascript·游戏·华为·electron·鸿蒙
llz_1127 小时前
web-第三次课后作业
前端·后端·web
遗憾随她而去.7 小时前
Web地图全体系深度梳理:引擎、数据源、图层、投影核心知识
前端
爱因斯坦乐8 小时前
Vue项目整合
前端·javascript·vue.js
FlyWIHTSKY8 小时前
TS、TSX、JS、JSX 文件扩展名详解
开发语言·javascript·ecmascript
无风听海8 小时前
IndexedDB 深度指南 浏览器中的事务型对象数据库
前端·数据库
ct9789 小时前
组件间的通信
前端·javascript·vue.js
左手吻左脸。9 小时前
Vue 全栈面试题大全(2026 最新版最详细)
前端·javascript·vue.js
Aphasia3119 小时前
手写KeepAlive组件
前端·react.js·面试