如何使用Google文档,CodeShare.io或Excalidraw工作等协作编辑平台?
这些平台提供实时协作,与其他用户同步以及解决冲突的机制。
可以通过使用WebSocket实现实时通信和文档的同步,因为它可以在用户进行更新时即可发送并接收到即时更新。例如,当用户A进行更改时,此信息将通过WebSocket发送到服务器,然后服务器将此更改广播到其他已连接的用户。
困难的部分是解决冲突。您可能会知道解决GIT存储库中合并冲突有多么困难。想象一下自动化此过程ð。
幸运的是,有许多大脑人员正在从事分布式冲突解决算法。
主要是有两种技术可以做到这一点:
- 操作转换(OT)
- 无冲突的复制数据类型(CRDT)
我会深入研究每种技术的算法细节,但我会引用this博客的几行。
区别在于,使用OT(操作转换),每当您编辑文档时,必须通过单个服务器发送编辑。 Google在Google文档的情况下提供了该服务器。因此,您所有的交流,您的所有协作都必须通过这一台服务器进行。
和CRDT是不同的,因为它们是分散的。他们不需要单个服务器才能工作。但是,您可以通过任何可用的网络同步设备。_
i为此项目选择了CRDT,因为有许多编写文档的开源实现。
鸟类的视野
所有平台都有类似的用户流,看起来像这样:
- 用户A创建新房间/文档
- 该应用程序提供了唯一的邀请URL /邀请代码< / li>
- 用户A向用户B 共享此邀请代码
- 用户b输入应用程序上的邀请代码,并加入房间/文档
- 用户A或用户B选择编程语言并开始编码
我将使用以下模式存储有关房间的相关信息:
interface Room {
roomId: string,
owner: string,
dateCreated: Date,
participants: string[],
programmingLanguage: string
}
当然,文档的内容也可以存储在数据库中。
现在,众所周知,我们知道需要存储的数据的模式,我们可以为房间操作设计API端点。
前端反应应用
i使用Vite和二手材料UI V5组件库生成了一个React + Typescript项目。
带有创建房间按钮的基本主页和加入房间输入字段就足够了,如下所示:
对于代码室页面,我们将使用YJS,这是一个CRDT实现,并具有编辑器绑定的编辑器绑定,例如散文镜,Quill,Quill,Monaco Editor等。
我们将在此应用程序中使用摩纳哥编辑器。因此,我们需要摩纳哥编辑器组件,用于YJS的React和Monaco编辑器结合。我们还需要YJS的WebSocket模块,这将传播到服务器的编辑器中的更改。以下是安装所有这些模块的命令:
npm install monaco-editor yjs y-monaco y-websocket
代码室页面
以下是代码室页面的代码:
在您的房间页面中创建摩纳哥编辑器组件
当编辑器组件安装时,我们创建y.doc(),这是共享数据结构
然后,我们通过y-websocket的WebSocketProvider连接到后端服务器,并与之设置共享数据类型。
最后,我们将摩纳哥编辑器绑定到此共享数据类型
import { useTheme } from "@mui/material";
import { useState, useRef, useEffect } from "react";
import { Editor } from "@monaco-editor/react";
import * as Y from 'yjs';
import { WebsocketProvider } from 'y-websocket';
import { MonacoBinding } from 'y-monaco';
import { editor } from "monaco-editor";
const serverWsUrl = import.meta.env.VITE_SERVER_WS_URL;
export default function CodeRoom() {
const theme = useTheme();
const editorRef = useRef<editor.IStandaloneCodeEditor>();
function handleEditorDidMount(editor: editor.IStandaloneCodeEditor) {
editorRef.current = editor;
// Initialize yjs
const doc = new Y.Doc(); // collection of shared objects
// Connect to peers with WebSocket
const provider: WebsocketProvider = new WebsocketProvider(serverWsUrl, "roomId", doc);
const type = doc.getText("monaco");
// Bind yjs doc to Manaco editor
const binding = new MonacoBinding(type, editorRef.current!.getModel()!, new Set([editorRef.current!]));
}
return (
<>
<Editor
height="100vh"
language={"cpp"}
defaultValue={"// your code here"}
theme={theme.palette.mode === "dark" ? "vs-dark" : "vs-light"}
onMount={handleEditorDidMount}
/>
</>
);
}
后端Express,Node.js,WebSocketServer
后端由一个简单的Express服务器和WebSocketServer组成,该服务器会倾听连接事件,并使用Y-Websocket模块提供的实用程序文件来设置连接。您可以探索SetupWsconnection文件以深入了解。
import express, { Request, Response } from 'express';
import { createServer } from 'http';
import cors from 'cors';
import { logger } from './logger';
import { WebSocketServer } from 'ws';
const setupWSConnection = require('y-websocket/bin/utils').setupWSConnection;
/**
* CORSConfiguration
*/
export const allowedOrigins = ['http://localhost:5173'];
/**
* Server INITIALIZATION and CONFIGURATION
* CORS configuration
* Request body parsing
*/
const app = express();
app.use(cors(
{
origin: allowedOrigins,
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
allowedHeaders: "Content-Type",
credentials: true
}
));
app.use(express.json());
/**
* Create an http server
*/
export const httpServer = createServer(app);
/**
* Create a wss (Web Socket Secure) server
*/
export const wss = new WebSocketServer({server: httpServer})
function onError(error: any) {
logger.info(error);
}
function onListening() {
logger.info("Listening")
}
httpServer.on('error', onError);
httpServer.on('listening', onListening);
/**
* On connection, use the utility file provided by y-websocket
*/
wss.on('connection', (ws, req) => {
logger.info("wss:connection");
setupWSConnection(ws, req);
})
和瞧!您现在有一个共享的代码编辑器。我在这里添加了更多功能:
- 在前端添加了编程语言的下拉列表,聆听用户后端上的语言更改事件,然后将其广播给其他同行。使用socket.io服务器和客户端创建了一个单独的WebSocket连接。
- 同样,每当新参与者进入房间时,参与者列表都会在前端显示,并且以同样的方式,此信息通过后端传播到其他同行。
- 使用MongoDB云实例连接后端服务器,用于存储房间信息。
演示
您可以通过访问此网站来使用此应用程序:
https://code-companion.netlify.app
或观看简短的演示视频:
此应用程序已部署在NetLify和AWS EC2上,并且数据库部署在MongoDB云上。
快乐编码:)