Yjs入门

作者: 我叫Aliya但是被占用了 | 来源:发表于2022-12-08 19:06 被阅读0次

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

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

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

demosAPI

// 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<object|boolean|Array|string|number|null|Uint8Array|Y.Type>)
    • 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<M>
    • 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<string,string>]) => eg: ytext.insert(0, 'bold text', { bold: true })
    • delete(index:number, length:number)
    • format(index:number, length:number, formattingAttributes:Object<string,string>) - 为文本中的区域指定格式属性
    • applyDelta(delta: Delta, opts:Object<string,any>)
    • 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<Y.Transaction>):void)
  • 合并或更新方法
    • Y.applyUpdate(Y.Doc, update:Uint8Array, [transactionOrigin:any])
    • Y.mergeUpdates(Array<Uint8Array>)
    • 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-clienthttps://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("<p>Example Text</p>");
});

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入门

      本文链接:https://www.haomeiwen.com/subject/kwuafdtx.html