Delta Format

Align
2023-08-18
4 min

Delta Format

德尔塔格式最初由Quill富文本编辑器描述并使用。yjs试用期来表述类序列数据的变化(例如Y.Text,Y.Array,Y.XmlFragment)。

使用德尔塔格式来描述yjs共享数据的的数据状态:

const ytext = ydoc.getText()

ytext.toDelta() // => []
ytext.insert(0, 'World', { bold: true })
ytext.insert(0, 'Hello ')
ytext.toDelta() // => [{ insert: 'Hello ' }, { insert: 'World', attributes: { bold: true } }]

在许多情况下,可以使用德尔塔格式对文档进行更改。例如在Y.Text中:

ytext.insert(0, 'Hello ')
ytext.insert(6, 'World', { bold: true })
// 等同于
ytext.applyDelta([{ insert: 'Hello ' }, { insert: 'World', attributes: { bold: true } }])

可以看到德尔塔格式使用了一些约定的描述/字段来表示不同的操作,下面我们来介绍下这些描述。

Delete

delta = [{
  delete: 3
}]

表示删除前三个字符

Retain

delta = [{
  retain: 1
}, {
  delete: 3
}]

表示保留一项,删除三项,比如下面这个例子:

ytext.insert(0, '12345')
ytext.applyDelta(delta)
ytext.toDelta() // => { insert: '15' }

(我的理解是一种序列化脚本,按顺序执行一次保留,执行三次删除)

Insert(在Y.Text中使用时)

插入的值应当始终为字符串,并且可以指定一些属性:

delta = [{
  retain: 1
}, {
  insert: 'abc', attributes: { bold: true }
}, {
  retain: 1
}, {
  insert: 'xyz'
}]

下面这个示例展示了在Y.Text文本下标0处插入123,随后保留(跳过)数字字符串1,在1和2之间插入abc并设置为粗体。随后保留(跳过)字符串2,在2和3之间插入xyz。 个人感觉retain在德尔塔数据中类似于编辑时的光标,retain: n表示光标移动多少个字符,操作都会在‘光标’所停的位置执行(没错,我感觉自己已经懂了^_^).

ytext.insert(0, '123')
ytext.applyDelta(delta)
ytext.toDelta() // => [{ insert: '1' },
                //     { insert: 'abc', attributes: { bold: true } },
                //     { insert: '2xyz3' }]

Retain(在Y.Text中使用时)

在Y.Text中使用Retain可能会包含一些属性:

delta = [{
  retain: 5, attributes: { italic: true }
}]

表示将前五个字符设置为斜体。包含属性时类似于选取并设置属性。

Insert (在 Y.Array & Y.XmlFragment上使用时)

插入的值应当是个数组:

yarray.observe(event => { console.log(event.changes.delta) })

yarray.insert(0, [1, 2, 3]) // => [{ insert: [1, 2, 3] }]
yarray.insert(2, ["abc"]) // => [{ retain: 2 }, { insert: ["abc"] }]
yarray.delete(0, 1) // => [{ delete: 1 }]

The delta format is very powerful to express changes that are performed in a Transaction. As explained in the shared types section, events are fired after transactions. With the delta format we can express multiple changes in a single event. E.g.

yarray.observe(event => { console.log(event.changes.delta) })

ydoc.transact(() => {
  // perform all changes in a single transaction
  yarray.insert(0, [1, 2, 3]) // => [{ insert: [1, 2, 3] }]
  yarray.insert(2, ["abc"]) // => [{ retain: 2 }, { insert: ["abc"] }]
  yarray.delete(0, 1) // => [{ delete: 1 }]
}) // => [{ insert: [2, "abc", 3] }]

ydoc.transact(() => {
  yarray.insert(0, ['x'])
  yarray.insert(2, ['y'])
}) // => [{ insert: ['x'] }, { retain: 1 }, { insert: ['y'] }]