Yjs入门

具有强大的共享数据抽象的 CRDT (conflict-free replicated data type 无冲突复制数据类型) 框架。它将其内部数据结构公开为共享类型。

共享类型是常见的数据类型,如具有超能力的 Map 或 Array:更改会自动分发到其他对等体,并在没有合并冲突的情况下进行合并。(带有数据劫持的 shareArray?)

支持许多现有的富文本编辑器、脱机编辑、版本快照、撤消/重做和共享光标。

demos、API

// demo from  https://docs.yjs.dev/
import * as Y from 'yjs'

const ydoc = new Y.Doc()
const ymap = ydoc.getMap()
ymap.set('keyA', 'valueA')

// 比如服务器?
const ydocRemote = new Y.Doc()
const ymapRemote = ydocRemote.getMap()
ymapRemote.set('keyB', 'valueB')

// Merge changes from remote
const update = Y.encodeStateAsUpdate(ydocRemote)
Y.applyUpdate(ydoc, update)

// Observe that the changes have merged
console.log(ymap.toJSON()) // => { keyA: 'valueA', keyB: 'valueB' }

共享类型

  • Y.Array - 支持在任何位置高效插入/删除元素。内部使用一个链表数组,并在必要时进行拆分。

    • length:number
    • insert(index:number, content:Array)
    • push(Array<同 insert 类型>)
    • unshift(Array<同 insert 类型>)
    • delete(index:number, length:number)
    • get(index:number)
    • slice(start:number, end:number):Array<同 insert 类型>
    • forEach(function(value: 同 insert 类型, index:number, array: Y.Array))
    • map(function(T, number, YArray):M):Array
    • toJSON
    • observe(function(YArrayEvent, Transaction):void)
    • unobserve
    • observeDeep
    • unobserveDeep
  • Y.Map

    • size: number
    • set(key:string, value:object|boolean|string|number|null|Uint8Array|Y.Type)
    • get(key:string | index:number)
    • delete(key)
    • has(key)
    • clear()
    • clone():Y.Map
    • toJSON
    • forEach
    • entries
    • values
    • keys
    • observe(function(YMapEvent, Transaction):void)
    • unobserve
    • observeDeep
    • unobserveDeep
  • Y.Text - 服务于富文本,可以转换为 delta 格式。

    • length:number
    • insert(index:number, content:string, [formattingAttributes:Object]) => eg: ytext.insert(0, 'bold text', { bold: true })
    • delete(index:number, length:number)
    • format(index:number, length:number, formattingAttributes:Object) - 为文本中的区域指定格式属性
    • applyDelta(delta: Delta, opts:Object)
    • toString
    • toJSON
    • toDelta
    • observe(function(YTextEvent, Transaction):void)
    • unobserve
    • observeDeep
    • unobserveDeep
  • Y.XmlFragment

  • Y.XmlElement

Y.Doc : const doc = new Y.Doc()

  • clientID - readonly unique id
  • gc - 是否启用垃圾收集。似乎与 undo 相关?
  • transact(function(Transaction):void [, origin:any]) - ??
  • get(string, Y.[TypeClass]):[Type]
  • getArray(string):Y.Array
  • getMap
  • getText
  • getXmlFragment
  • on
  • off
    • on('update', function(updateMessage:Uint8Array, origin:any, Y.Doc):void)
    • on('beforeTransaction', function(Y.Transaction, Y.Doc):void)
    • on('afterTransaction',
    • on('beforeAllTransactions', function(Y.Doc):void)
    • on('afterAllTransactions', function(Y.Doc, Array):void)
  • 合并或更新方法
    • Y.applyUpdate(Y.Doc, update:Uint8Array, [transactionOrigin:any])
    • Y.mergeUpdates(Array)
    • Y.diffUpdate(update: Uint8Array, stateVector: Uint8Array): Uint8Array
    • Y.UndoManager
    • ...

https://github.com/yjs/yjs#Shared-Types

y-websocket

maybe https://github.com/y-js/y-websockets-client 、 https://github.com/y-js/y-websockets-server ?

  • 支持跨选项卡通信。当您在同一浏览器中打开同一文档时,文档上的更改将通过跨选项卡通信(Broadcast Channel 和 localStorage 作为回退)进行交换。

  • 支持意识信息的交换(例如光标)。

// Extension for tiptap
import { keymap } from "prosemirror-keymap";
import { Extension } from "tiptap";
import {
  redo,
  undo,
  yCursorPlugin,
  ySyncPlugin,
  yUndoPlugin,
} from "y-prosemirror";
import { WebsocketProvider } from "y-websocket";
import * as Y from "yjs";

const ydoc = new Y.Doc();
const roomname = location.href.includes("uuu") ? "tiptap-demo2" : "tiptap-demo";
const provider = new WebsocketProvider("ws://localhost:8080", roomname, ydoc);
const type = ydoc.getXmlFragment("prosemirror");

provider.on("sync", (isSynced) => {
  console.log("======= sync", isSynced, window.ee.getHTML()); // logs "connected" or "disconnected"
  //   现在没值时进行插入
  if (window.ee.getText() === "")
    window.ee.commands.setContent("

Example Text

"); }); export default class RealtimeExtension extends Extension { get name() { return "realtime"; } get plugins() { return [ ySyncPlugin(type), yCursorPlugin(provider.awareness), yUndoPlugin(), keymap({ "Mod-z": undo, "Mod-y": redo, "Mod-Shift-z": redo, }), ]; } }
// server by node
const WebSocket = require("ws");
const http = require("http");
const StaticServer = require("node-static").Server;
const setupWSConnection = require("y-websocket/bin/utils.js").setupWSConnection;

const production = process.env.PRODUCTION != null;
const port = process.env.PORT || 8080;

const staticServer = new StaticServer("../", {
  cache: production ? 3600 : false,
  gzip: production,
});

const server = http.createServer((request, response) => {
  request
    .addListener("end", () => {
      staticServer.serve(request, response);
    })
    .resume();
});
const wss = new WebSocket.Server({ server });

wss.on("connection", (conn, req) => setupWSConnection(conn, req, { gc: true }));

server.listen(port);

console.log(
  `Listening to http://localhost:${port} ${production ? "(production)" : ""}`
);

你可能感兴趣的:(Yjs入门)