API
import * as Y from 'yjs'
共享类型
Y.Array
一种可共享的类数组类型,支持在任何位置插入/删除元素。在内部,它使用了数组链表,必要时可以分割数组。
const yarray = new Y.Array() 
insert(index:number, content:Array<object|boolean|Array|string|number|Uint8Array|Y.Type>)
 在index处插入内容。请注意内容是元素的数组。array.insert(0, [1])在索引0处拼接列表并插入1。
push(Array<Object|boolean|Array|string|number|Uint8Array|Y.Type>)
 unshift(Array<Object|boolean|Array|string|number|Uint8Array|Y.Type>)
 delete(index:number, length:number)
 get(index:number)
 length:number
 forEach(function(value:object|boolean|Array|string|number|Uint8Array|Y.Type, index:number, array: Y.Array))
 map(function(T, number, YArray):M):Array<M>
 toArray():Array<object|boolean|Array|string|number|Uint8Array|Y.Type>
 将该YArray的内容拷贝到一个新数组中。
toJSON():Array<Object|boolean|Array|string|number>
 将该YArray的内容拷贝到一个新数组中。该方法会使用toJSON方法将所有子类型转换为 JSON。
[Symbol.Iterator]
 返回一个YArray迭代器,其中包含了数组中每个索引的值。
for (let value of yarray) { .. }
observe(function(YArrayEvent, Transaction):void)
 向该类型添加一个事件侦听器,该事件侦听器将在每次修改这个类型时同步调用。如果在事件侦听器中修改了此类型,则在当前事件侦听器返回后将再次调用事件侦听器。
unobserve(function(YArrayEvent, Transaction):void)
 从该类型中删除observe事件侦听器。
observeDeep(function(Array<YEvent>, Transaction):void)
 向该类型添加一个事件侦听器,该事件侦听器将在每次修改此类型或其任何子类型时同步调用。如果在事件侦听器中修改了此类型,则在当前事件侦听器返回后将再次调用事件侦听器。事件侦听器接收自己或其子程序创建的所有事件。
unobserveDeep(function(Array<YEvent>, Transaction):void)
 从该类型中删除observeDeep事件监听器。
Y.Map
共享 Map 类型。
const ymap = new Y.Map()
get(key:string):object|boolean|string|number|Uint8Array|Y.Type
 set(key:string, value:object|boolean|string|number|Uint8Array|Y.Type)
 delete(key:string)
 has(key:string):boolean
 get(index:number)
 toJSON():Object<string, Object|boolean|Array|string|number|Uint8Array>
 将该YMap的[key,value]对复制到一个新对象。它使用toJSON方法将所有子类型转换为JSON。
forEach(function(value:object|boolean|Array|string|number|Uint8Array|Y.Type, key:string, map: Y.Map))
 对每个键值对执行一次传入的函数。
[Symbol.Iterator]
 返回迭代器的[key, value]对。
for (let [key, value] of ymap) { .. }
entries()
 返回迭代器的[key, value]对。
values
 返回迭代器的所有值。
keys
 返回迭代器的所有键。
observe(function(YMapEvent, Transaction):void)
 向该类型添加一个事件侦听器,该事件侦听器将在每次修改这个类型时同步调用。如果在事件侦听器中修改了此类型,则在当前事件侦听器返回后将再次调用事件侦听器。
unobserve(function(YMapEvent, Transaction):void)
 从该类型中删除observe事件侦听器。
observeDeep(function(Array<YEvent>, Transaction):void)
 向该类型添加一个事件侦听器,该事件侦听器将在每次修改此类型或其任何子类型时同步调用。如果在事件侦听器中修改了此类型,则在当前事件侦听器返回后将再次调用事件侦听器。事件侦听器接收自己或其子程序创建的所有事件。
unobserveDeep(function(Array<YEvent>, Transaction):void)
 从该类型中删除observeDeep事件监听器。
Y.Text
为文本的共享编辑而优化的可共享类型。它允许在文本中将属性分配给范围。这让视线该类型的服务文绑定成为可能。
该类型还可以转换为delta format。同样地,YTextEvents计算增量变化。
const ytext = new Y.Text()
insert(index:number, content:string, [formattingAttributes:Object<string,string>])
 在index处插入一个字符串,并为其分配格式化属性。
ytext.insert(0, 'bold text', { bold: true })
delete(index:number, length:number)
 format(index:number, length:number, formattingAttributes:Object<string,string>)
 为文本中的的一定范围指定格式化属性。
applyDelta(delta, opts:Object<string,any>)
 参见Quill Delta可以设置防止删除结束换行的配置项,默认为true。
ytext.applyDelta(delta, { sanitize: false })
length:number
 toString():string
 在不使用格式化配置项的情况下,将此类型转换为字符串。
toJSON():string
 参见toString
toDelta():Delta
 将该类型转换为Quill Delta
observe(function(YTextEvent, Transaction):void)
 向该类型添加一个事件侦听器,该事件侦听器将在每次修改这个类型时同步调用。如果在事件侦听器中修改了此类型,则在当前事件侦听器返回后将再次调用事件侦听器。
unobserve(function(YTextEvent, Transaction):void)
 从该类型中删除observe事件侦听器。
observeDeep(function(Array<YEvent>, Transaction):void)
 向该类型添加一个事件侦听器,该事件侦听器将在每次修改此类型或其任何子类型时同步调用。如果在事件侦听器中修改了此类型,则在当前事件侦听器返回后将再次调用事件侦听器。事件侦听器接收自己或其子程序创建的所有事件。
unobserveDeep(function(Array<YEvent>, Transaction):void)
 从该类型中删除observeDeep事件监听器。
Y.XmlFragment
包含Y.XmlElements数组的容器。
const yxml = new Y.XmlFragment()
insert(index:number, content:Array<Y.XmlElement|Y.XmlText>)
 delete(index:number, length:number)
 get(index:number)
 length:number
 toArray():Array<Y.XmlElement|Y.XmlText>
 将子元素拷贝到一个新数组。
toDOM():DocumentFragment
 将此类型和所有子元素转换为新的 DOM 元素。
toString():string
 获取所有后代的 XML 序列化。
toJSON():string
 参见toString
observe(function(YXmlEvent, Transaction):void)
 向该类型添加一个事件侦听器,该事件侦听器将在每次修改这个类型时同步调用。如果在事件侦听器中修改了此类型,则在当前事件侦听器返回后将再次调用事件侦听器。
unobserve(function(YXmlEvent, Transaction):void)
 从该类型中删除observe事件侦听器。
observeDeep(function(Array<YEvent>, Transaction):void)
 向该类型添加一个事件侦听器,该事件侦听器将在每次修改此类型或其任何子类型时同步调用。如果在事件侦听器中修改了此类型,则在当前事件侦听器返回后将再次调用事件侦听器。事件侦听器接收自己或其子程序创建的所有事件。
unobserveDeep(function(Array<YEvent>, Transaction):void)
 从该类型中删除observeDeep事件监听器。
T.XmlElement
表示 XML 元素的可共享类型。它拥有nodeName、属性和一系列子节点。但是它不需要验证其内容,也不需要遵循 XML。
const yxml = new Y.XmlElement()
insert(index:number, content:Array<Y.XmlElement|Y.XmlText>)
 delete(index:number, length:number)
 get(index:number)
 length:number
 setAttribute(attributeName:string, attributeValue:string)
 removeAttribute(attributeName:string)
 getAttribute(attributeName:string):string
 getAttributes(attributeName:string):Object<string,string>
 toArray():Array<Y.XmlElement|Y.XmlText>
 将子元素拷贝到一个新数组。
toDOM():Element
 将此类型和所有子元素转换为新的 DOM 元素。
toString():string
 获取所有后代的 XML 序列化。
toJSON():string
 参见toString。
observe(function(YXmlEvent, Transaction):void)
 向该类型添加一个事件侦听器,该事件侦听器将在每次修改这个类型时同步调用。如果在事件侦听器中修改了此类型,则在当前事件侦听器返回后将再次调用事件侦听器。
unobserve(function(YXmlEvent, Transaction):void)
 从该类型中删除observe事件侦听器。
observeDeep(function(Array<YEvent>, Transaction):void)
 向该类型添加一个事件侦听器,该事件侦听器将在每次修改此类型或其任何子类型时同步调用。如果在事件侦听器中修改了此类型,则在当前事件侦听器返回后将再次调用事件侦听器。事件侦听器接收自己或其子程序创建的所有事件。
unobserveDeep(function(Array<YEvent>, Transaction):void)
 从该类型中删除observeDeep事件监听器。
Y.Doc
const doc = new Y.Doc()
clientID readonly
 表示客户的唯一ID。
gc
 是否在该 doc 实例上启用了垃圾回收机制(Garbage Collection)。设置doc.gc = false以禁用垃圾回收并能够恢复旧内容。
transact(function(Transaction):void [, origin:any])
 对共享文档的每个更改都发生在一个事件流程(transaction)中。观察者调用和更新事物是在每个事件流程后。您应该将更改放到单个事件流程中以降低事件调用的数量。举个例子,doc.transact(() => { yarray.insert(..); ymap.set(..) })触发单个的更改事件。
您可以通过设置可选参数origin,存储在transaction.origin和on('update', (update, origin) => ..)。
toJSON():any
 将整个文档转换为 js 对象,递归遍历每种 yjs 类型。
get(string, Y.[TypeClass]):[Type]
 定义一个共享类型。
getArray(string):Y.Array
 定义一个共享的 Y.Array 类型。等同于y.get(string, Y.Array)。
getMap(string):Y.Map
 定义一个共享的 Y.Map 类型。等同于y.get(string, Y.Map)。
getXmlFragment(string):Y.XmlFragment
 定义一个共享的 Y.XmlFragment 类型。等同于y.get(string, Y.XmlFragment)。
on(string, function)
在共享类型上注册事件侦听器。
off(string, function)
解绑共享类型上的事件侦听器。
Y.Doc 事件
on('update', function(updateMessage:Uint8Array, origin:any, Y.Doc):void)
 侦听文档更新。文档在更新时必须传输到所有其他的展示端。您可以不限顺序且多次的更新文档。
on('beforeTransaction', function(Y.Transaction, Y.Doc):void)
 每个事件流程开始前派发。
on('afterTransaction', function(Y.Transaction, Y.Doc):void)
 每个事件流程结束后派发。
on('beforeAllTransactions', function(Y.Doc):void)
 事件流程可以嵌套(例如,在一个事件流程中调用另一个事件流程)。在首个事件流程前派发。
on('afterAllTransactions', function(Y.Doc, Array<Y.Transaction>):void)
 在最后一个事件流程结束后派发。
文档更新
共享文档上的更改被编码到文档更新中。文档更新是可交换且幂等的。这意味着它们可以以任何顺序应用且应用多次。
示例:侦听更新事件并将其应用于远程客户端
const doc1 = new Y.Doc()
const doc2 = new Y.Doc()
doc1.on('update', update => {
  Y.applyUpdate(doc2, update)
})
doc2.on('update', update => {
  Y.applyUpdate(doc1, update)
})
// 所有的更改也更新到了其他文档中
doc1.getArray('myarray').insert(0, ['Hello doc2, you got this?'])
doc2.getArray('myarray').get(0) // => 'Hello doc2, you got this?'
2
3
4
5
6
7
8
9
10
11
12
13
14
Yjs 在内部维护了**“状态向量”(后面会介绍),用以表示每个客户端的下一个期望时钟。它保存了每个客户端创建的结构数量。当两个客户端同步时,您既可以交换完整的文档结构,也可以通过发送“状态向量”**来计算差异。
示例:通过交换完整的文档结构同步两个客户端
const state1 = Y.encodeStateAsUpdate(ydoc1)
const state2 = Y.encodeStateAsUpdate(ydoc2)
Y.applyUpdate(ydoc1, state2)
Y.applyUpdate(ydoc2, state1)
2
3
4
示例:通过计算差异同步两个客户端
这个例子展示了如何通过使用远程客户端的“状态向量”来计算差异,从而用最少的交换数据同步两个客户端。使用状态向量同步客户端需要另一次往返,但可以保证很大的带宽安全。
const stateVector1 = Y.encodeStateVector(ydoc1)
const stateVector2 = Y.encodeStateVector(ydoc2)
const diff1 = Y.encodeStateAsUpdate(ydoc1, stateVector2)
const diff2 = Y.encodeStateAsUpdate(ydoc2, stateVector1)
Y.applyUpdate(ydoc1, diff2)
Y.applyUpdate(ydoc2, diff1)
2
3
4
5
6
Y.applyUpdate(Y.Doc, update:Uint8Array, [transactionOrigin:any])
 对共享文档应用更新。您可以选择设置transactionOrigin从而将其存储到transaction.Origin和ydoc.on('update', (update, origin) => ..)
Y.encodeStateAsUpdate(Y.Doc, [encodedTargetStateVector:Uint8Array]):Uint8Array
 将文档状态编码为可应用于远程文档的单个更新消息。可以选择指定目标状态向量,以便只将差异写入更新消息。
Y.encodeStateVector(Y.Doc):Uint8Array
 计算状态向量并将其编码为 Uint8Array。
相对位置
警告
该API尚不稳定。
该特性用于管理选择的内容/光标。当和操作文档的其它用户一起工作时,请不要相信索引位置是你以为的位置。相对位置会始终保持在共享文档的元素上,不受远程更改的影响。举个例子,在文档中位置是“a|c”则相对位置就会判断到c。若远程用户在光标签插入了文档,光标仍旧会停留在c前。insert(1, 'x')("a|c") = "ax|c"。当相对位置设置为文档结尾,那么光标就会一直停留在文档结尾了。
示例:移动到相对位置并返回
const relPos = Y.createRelativePositionFromTypeIndex(ytext, 2)
const pos = Y.createAbsolutePositionFromRelativePosition(relPos, doc)
pos.type === ytext // => true
pos.index === 2 // => true
2
3
4
示例:向远程客户端发送相对位置(json)
const relPos = Y.createRelativePositionFromTypeIndex(ytext, 2)
const encodedRelPos = JSON.stringify(relPos)
// 向远程客户端发送 encodedRelPos。
const parsedRelPos = JSON.parse(encodedRelPos)
const pos = Y.createAbsolutePositionFromRelativePosition(parsedRelPos, remoteDoc)
pos.type === remoteytext // => true
pos.index === 2 // => true
2
3
4
5
6
7
示例:发送相对位置到远程客户端(Uint8Array)
const relPos = Y.createRelativePositionFromTypeIndex(ytext, 2)
const encodedRelPos = Y.encodeRelativePosition(relPos)
// 向远程客户端发送 encodedRelPos。
const parsedRelPos = Y.decodeRelativePosition(encodedRelPos)
const pos = Y.createAbsolutePositionFromRelativePosition(parsedRelPos, remoteDoc)
pos.type === remoteytext // => true
pos.index === 2 // => true
2
3
4
5
6
7
Y.createRelativePositionFromTypeIndex(Uint8Array|Y.Type, number)
 Y.createAbsolutePositionFromRelativePosition(RelativePosition, Y.Doc)
 Y.encodeRelativePosition(RelativePosition):Uint8Array
 Y.decodeRelativePosition(Uint8Array):RelativePosition
 Y.UndoManager
Yjs 自带了撤销/重做管理器,用以应对 Yjs 类型的撤销/重做。可以选择将更改的范围限定到起始事件流程。
const ytext = doc.getText('text')
const undoManager = new Y.UndoManager(ytext)
ytext.insert(0, 'abc')
undoManager.undo()
ytext.toString() // => ''
undoManager.redo()
ytext.toString() // => 'abc'
2
3
4
5
6
7
8
constructor(scope:Y.AbstractType|Array<Y.AbstractType> [, {captureTimeout:number,trackedOrigins:Set<any>,deleteFilter:function(item):boolean}])
 可以接受单个类型作为范围,也可以接受类型数组。
undo()
 redo()
 stopCapturing()
 on('stack-item-added', { stackItem: { meta: Map<any,any> }, type: 'undo' | 'redo' })
 注册当StackItem添加到撤销栈或重做栈时调用的事件。
on('stack-item-popped', { stackItem: { meta: Map<any,any> }, type: 'undo' | 'redo' })
 注册当StackItem从撤销栈或重做栈移出时调用的事件。
示例:停止捕获
如果创建的时间间隔小于options.captureTimeout,则UndoManager会合并Undo-StackItem。调用um.stopCapturing()可以避免下一个StackItem被合并。
// 无停止捕获
ytext.insert(0, 'a')
ytext.insert(1, 'b')
undoManager.undo()
ytext.toString() // => '' (注意 'ab' 被移除了)
// 带停止捕获
ytext.insert(0, 'a')
undoManager.stopCapturing()
ytext.insert(0, 'b')
undoManager.undo()
ytext.toString() // => 'a' (注意只有 'b' 被)
2
3
4
5
6
7
8
9
10
11
示例:指定追踪的起始点
共享文档上的每个更改都有一个起始点。如果没有指定起始点,则默认为null。通过指定trackedOrigins您可以有选择地指定UndoManager应该跟踪哪些更改。UndoManager实例总会被添加到trackedOrigins中。
class CustomBinding {}
const ytext = doc.getText('text')
const undoManager = new Y.UndoManager(ytext, {
  trackedOrigins: new Set([42, CustomBinding])
})
ytext.insert(0, 'abc')
undoManager.undo()
ytext.toString() // => 'abc' (不跟踪,因为起始点“null”并且不是“trackedTransactionOrigins”的一部分)
ytext.delete(0, 3) // 恢复
doc.transact(() => {
  ytext.insert(0, 'abc')
}, 42)
undoManager.undo()
ytext.toString() // => '' (跟踪,因为起始点是“trackedTransactionOrigins”的一部分)
doc.transact(() => {
  ytext.insert(0, 'abc')
}, 41)
undoManager.undo()
ytext.toString() // => '' (不跟踪,因为 41 不是“trackedTransactionOrigins”的一部分)
ytext.delete(0, 3) // 恢复
doc.transact(() => {
  ytext.insert(0, 'abc')
}, new CustomBinding())
undoManager.undo()
ytext.toString() // => '' (跟踪,因为起始点 `CustomBinding` 和 `CustomBinding`是 `trackedTransactionorigins`的一部分)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
示例:向StackItems添加其他信息
在撤消或重做以前的操作时,通常需要恢复其他元信息,如光标位置或文档上的视图。您可以为Undo-/Redo-StackItems分配元信息。
const ytext = doc.getText('text')
const undoManager = new Y.UndoManager(ytext, {
  trackedOrigins: new Set([42, CustomBinding])
})
undoManager.on('stack-item-added', event => {
  // 保存 stack-item 的当前光标位置
  event.stackItem.meta.set('cursor-location', getRelativeCursorLocation())
})
undoManager.on('stack-item-popped', event => {
  // 储存 stack-item 的当前光标位置
  restoreCursorLocation(event.stackItem.meta.get('cursor-location'))
})
2
3
4
5
6
7
8
9
10
11
12
13
14
