使用yjs实现协同demo搭建
摘要:本文介绍了基于CRDT算法的Yjs库在多人协同开发中的应用。Yjs通过WebSocket实现数据同步,服务端代码演示了两种创建WebSocket服务的方法。客户端通过Y.Doc创建共享数据结构,使用WebsocketProvider建立连接,并利用awareness模块管理用户状态。示例展示了如何监听数据变化并同步节点操作(如添加、移动节点)。该方案为构建实时协作应用提供了成熟的技术实现,适
·
多人协同开发确实是比较难的知识点,在技术实现上有一定挑战,但随着各种技术库的发展,目前已经有了比较成熟的解决方案。今介绍 Yjs 基于CRDT算法,用于构建自动同步的协作应用程序
yjs官网:https://docs.yjs.dev/
服务端 server.js
const { WebSocketServer } = require("ws");
// 创建 yjs ws 服务
const yjsws = new WebSocketServer({ port: 1234 });
yjsws.on("connection", (conn, req) => {
console.log(req.url); // 标识每一个连接用户,用于广播不同的文件协同
conn.onmessage = (event) => {
yjsws.clients.forEach((conn) => {
conn.send(event.data);
});
};
conn.on("close", (conn) => {
console.log("yjs 用户关闭连接");
});
});
yjs官网给的server.js
const Websocket = require("ws");
const { setupWSConnection } = require("y-websocket/bin/utils");
const port = 1234;
// 创建一个 ws 服务
const wss = new Websocket.Server({ port });
// 当服务连接时,调用 setupWSConnection 方法,将 y-websocket 的配置传入
wss.on("connection", (ws) => {
setupWSConnection(ws, wss);
});
console.log(`Server started on ws://localhost:${port}`);
web客户端使用
import { ref, onMounted, onBeforeUnmount, watch } from "vue";
import * as Y from "yjs";
import { WebsocketProvider } from "y-websocket";
import { useVueFlow } from '@vue-flow/core'
export default () => {
const { addNodes, findNode, updateNode } = useVueFlow();
const ydoc = new Y.Doc();
// 初始化传输
const provider = new WebsocketProvider(
"ws://localhost:1234",
"collaborative-editor",
ydoc
);
// 共享数据类型
// 在 Yjs 文档中创建共享的 nodes 和 edges 数组
const ymap = ydoc.getMap('flow-data')
const randomColor = () => {
return (
"#" +
Math.floor(Math.random() * 0xffffff)
.toString(16)
.padEnd(6, "0")
);
};
provider.on('status', event => {
console.log('provider status', event.status) // logs "connected" or "disconnected"
})
provider.awareness.setLocalStateField("user", {
name: "heyi-" + Math.random(),
color: randomColor(),
});
onMounted(() => {
const localUser = provider.awareness.getLocalState()?.user;
const remoteUsers = provider.awareness.getStates();
watch(() => provider.awareness.getStates(), (val) => {
console.log('watch provider.awareness.getStates()', val)
}, { deep: true, immediate: true })
provider.awareness.on('change', (changes: any) => {
// Whenever somebody updates their awareness information,
// we log all awareness information from all users.
console.log('Awareness changed:', changes);
console.log('Array.from(provider.awareness.getStates().values())', Array.from(provider.awareness.getStates().values()));
})
ymap.observe(({ transaction, changes }) => {
// if (!transaction.origin) return; // 没有 origin 表示的是本地发起
changes.keys.forEach((change, key) => {
console.log('observe change-->', change)
console.log('observe key-->', key)
console.log('ymap.get(key)-->', ymap.get(key))
// YjsHandle({
// change,
// key,
// value: ymap.get(key)
// });
});
});
ydoc.on("update", (data) => {
// console.log("Yjs document updated:", data);
});
})
onBeforeUnmount(() => {
provider.destroy();
ydoc.destroy();
});
function YjsHandle({ change, key, value }: { change: any, key: any, value: any }) {
switch (key) {
case "addNode":
addNodes([value]);
break;
case "position":
const node = findNode(value.id);
if (node) {
node.position = value.position;
node.data.updateTime = new Date().getTime();
updateNode(node.id, node);
}
break;
default:
break;
}
}
return {
ymap
}
}
使用
onNodesChange((changes) => {
changes.forEach((change) => {
if (change.type === 'add') {
ymap.set('addNode', change.item)
}
if (change.type === 'position') {
ymap.set('position', {
id: change.id,
position: change.position
})
}
})
})
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)