Changes on the shared document are encoded into document updates. Document updates are commutative, associative, and idempotent. This means that you can apply them in any order and multiple times. All clients will sync up when they received all document updates. In Yjs, clients can also sync up by computing a state vector and then exchanging the differences.
Y.applyUpdate(Y.Doc, update:Uint8Array, [transactionOrigin:any])
Apply a document update on the shared document. Optionally you can specify transactionOrigin
that will be stored on transaction.origin
and ydoc.on('update', (update, origin) => ..)
.
Y.encodeStateAsUpdate(Y.Doc, [encodedTargetStateVector:Uint8Array]): Uint8Array
Encode the document state as a single update message that can be applied on the remote document. Optionally, specify the target state vector to only write the differences to the update message.
Y.encodeStateVector(Y.Doc): Uint8Array
Computes the state vector and encodes it into an Uint8Array.
ydoc.on('update', eventHandler: function(update: Uint8Array, origin: any, doc: Y.Doc))
Listen to incremental updates on the Yjs document. This is part of the Y.Doc API. Send the computed incremental update to all connected clients, or store it in a database.
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)})โ// All changes are also applied to the other documentdoc1.getArray('myarray').insert(0, ['Hello doc2, you got this?'])doc2.getArray('myarray').get(0) // => 'Hello doc2, you got this?'
Yjs internally maintains a state vector that denotes the next expected clock from each client. In a different interpretation, it holds the number of modifications created by each client. When two clients sync, you can either exchange the complete document structure or only the differences by sending the state vector to compute the differences.
Example: Sync two clients by exchanging the complete document structure
const state1 = Y.encodeStateAsUpdate(ydoc1)const state2 = Y.encodeStateAsUpdate(ydoc2)Y.applyUpdate(ydoc1, state2)Y.applyUpdate(ydoc2, state1)
Example: Sync two clients by computing the differences
This example shows how to sync two clients with a minimal amount of data exchanged by computing the differences using the state vector of the remote client. Syncing clients using the state vector requires another roundtrip but can save a lot of bandwidth.
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)
We compress document updates to a highly compressed binary format. Therefore, document updates are represented as Uint8Arrays. An Uint8Array
represents binary data similarly to a NodeJS' Buffer . The difference is that Uint8Array
is available in all JavaScript environments. The catch is that you can't JSON.stringify
/JSON.parse
the data because there is no JSON representation for binary data. However, most communication protocols support binary data. If you still need to transform the data into a string, you can use Base64 encoding. For example, by using the js-base64
library:
import { fromUint8Array, toUint8Array } from 'js-base64'โconst documentState = Y.encodeStateAsUpdate(ydoc) // is a Uint8Array// Transform Uint8Array to a Base64-Stringconst base64Encoded = fromUint8Array(documentState)// Transform Base64-String back to an Uint8Arrayconst binaryEncoded = toUint8Array(base64Encoded)
npm install js-base64
โ