TinyEngine 低代码实时协作技术详解:原理与实操
前言
一般的多人协作业务需求通常是针对文档、表格或制图等,场景相对简单,协同操作的对象为文字或图片,对象比较单一。乍一看,低代码的多人协作似乎无从下手。因为低代码不仅涉及页面 Canvas 中文字属性的同步,还涵盖组件拖拽、样式设置、事件绑定、高级属性配置,甚至是代码协同编辑与同步。那么,我们是如何在低代码这个复杂场景下实现多人协同编辑的呢?
TinyEngine 低代码引擎多人协同技术详解
一、底层逻辑:CRDT(无冲突复制数据类型)
CRDT(Conflict-free Replicated Data Type)是一种允许并发修改、自动合并且永不冲突的数据结构。即使多个用户同时编辑同一份文档、表格或图形,系统也能在之后自动合并出一致的结果,不需要'锁'或'人工解决冲突'。
一个例子
假设一个协作文本编辑器有两个用户:
- A 插入'Hello '
- B 插入'World!'
在普通系统中,如果两个操作几乎同时发生,可能导致冲突。但在 CRDT 模型下,每个操作都可合并:系统会基于操作的逻辑时间或唯一标识符自动确定合并顺序;最终所有节点都会收敛到相同的状态,如'Hello World!'。
CRDT 的两种主要类型
- State-based(状态型 CRDT)
- 每个节点维护完整的状态副本,并定期将状态合并:
local_state = merge(local_state, remote_state)
- 每个节点维护完整的状态副本,并定期将状态合并:
- Operation-based(操作型 CRDT)
- 每个节点只传播'操作'(如'加 1'、'插入字符 X'),其他节点按相同逻辑执行该操作。
在 TinyEngine 项目中,我们采用的是操作型 CRDT(Operation-based CRDT)库 Yjs。
在 Yjs 中,每个协同文档对应一个根对象 Y.Doc,它可以包含多种可协同的数据结构,例如 Y.Array、Y.Map、Y.Text 等。每个客户端都维护一份本地的 Y.Doc 副本,这些副本通过 Yjs 的同步机制保持一致。当多个客户端通过 y-websocket provider 连接到同一个房间(room)时,它们会共享相同的文档数据。任何客户端对文档的修改(如插入、删除、更新)都会被编码为操作(operation),并广播到其他客户端,从而实现实时的数据同步。
二、从数据结构到协同模型:tiny-engine 的页面 Schema 与 Yjs 的结合
无论是哪一种类型的 CRDT,其核心都离不开一个健全且完备的数据结构。对于 tiny-engine 来说,低代码页面本身也是由一套结构化的数据所描述的。这套数据结构不仅要支持页面的层级关系(如区块、组件、插槽),还要能够表达页面的动态逻辑(如循环、条件、生命周期、数据源等)。
在 tiny-engine 中,页面的基础结构可以抽象为以下两个 TypeScript 接口:
// 节点类型
export interface Node {
id: string;
componentName: string;
props: Record<string, > & { ?: { ?: <, > }[] };
?: [];
?: | | ;
?: | <, >;
?: [];
?: <, >;
?: [];
?: | <, >;
}
= <, > & {
?: ;
?: ;
?: ;
?: <, >;
?: <, >;
?: <, >;
?: ;
?: ;
?: [];
?: [];
?: ;
};


