写了个y-mxgraph:给 draw.io 接上了 Yjs,顺便解决了部署在 iframe 里的一堆问题

一句话:Yjs binding for draw.io

它把 draw.io 的 XML 文件格式映射到 Yjs 的 shared types,实现双向同步:

  • 源码地址:github.com/mizuka-wu/y-mxgraph
  • 在线 Demo 预览:mizuka-wu.github.io/y-mxgraph/demo/
ts 复制代码
import * as Y from 'yjs';
import { Binding } from 'y-mxgraph';

const doc = new Y.Doc();
const provider = new WebrtcProvider('my-room', doc);

App.main((app) => {
  const binding = new Binding(app.currentFile, { doc });
});

几行代码,你的 draw.io 编辑器就变成了多人协作白板。其他人的光标会实时显示,编辑内容秒级同步,undo/redo 跨客户端工作。


为什么要做 iframe-bridge

上面的代码是「单页模式」------draw.io 直接跑在当前页面里。但很多场景下这行不通:

1. draw.io 的全局污染

draw.io 的脚本加载后会在 window 上挂载大量全局变量(mxBasePathEditormxConstants 等)。如果你的主应用也是一个复杂的前端项目(比如 React + 各种第三方库),这些全局变量可能会和 draw.io 冲突。更糟糕的是,draw.io 的 CSS 也是全局的,样式覆盖问题很难根治。

2. 多实例需求

你可能需要在一个页面里并排放两个画板(比如对比两个版本的架构图)。没有 iframe,两个 draw.io 实例会共享同一个 window,状态互相干扰。

3. 安全边界

在 SaaS 产品中,你不希望用户的 JavaScript 能直接访问 draw.io 的内部 API。iframe 提供了天然的执行环境隔离。

所以问题变成了:iframe 里的 draw.io 怎么和 iframe 外的 Yjs 同步?


iframe-bridge 的设计

y-mxgraph 提供了一个 iframe-bridge 子包,把 Yjs 同步的逻辑封装成两个角色:

  • Server(parent page):持有真实的 Y.Doc、Awareness 和网络 Provider(y-webrtc / y-websocket)
  • Provider (iframe child):维护一份本地 Y.Doc,通过 postMessage 和 Server 交换 update
ts 复制代码
// Parent page
import { createIframeBridgeServer } from 'y-mxgraph/iframe-bridge/server';

const bridge = createIframeBridgeServer(iframe, doc, awareness);

// Iframe child
import { createIframeBridgeProvider } from 'y-mxgraph/iframe-bridge/provider';

const bridge = createIframeBridgeProvider(doc);
const binding = new Binding(file, { doc, awareness: bridge.awareness });

这段看起来简单的代码,背后处理了几个不简单的脏活:

Awareness clientID 映射:iframe 和 parent 各自有独立的 Y.Doc,clientID 不同。直接透传 awareness update 会导致 iframe 里的协作光标显示异常(颜色/用户名错乱)。bridge 在底层做了二进制级别的 clientID remap,把 parent 的 clientID 映射到 iframe 本地。

undo/redo 跨 iframedraw.io 自带的 undoManager 只在单个实例内工作。bridge 通过 takeoverUndoManager 把它代理到 parent 的 Y.UndoManager,这样 A 在 iframe-1 里 undo,iframe-2 里也会同步回退。

基线过滤 :iframe 启动时从 parent 接收的初始状态不能进 undo 栈,否则用户按 Ctrl+Z 会把文档撤销成空文档。bridge 用 BASELINE_ORIGIN 标记区分初始同步和真实编辑。

连接重试 :iframe 加载通常比 parent 快,provider 启动后如果 server 还没创建好,会自动重试 init 直到连上。


除了 iframe-bridge,y-mxgraph 还做了什么

  • 多页 diagram 同步draw.io 支持多页文件(一个 .drawio 里多个 diagram),y-mxgraph 用 Y.Map + Y.Array 完整映射了这种结构
  • 初始内容策略:replace / merge-remote / merge-client,应对多人同时打开空文档的场景
  • 绕过 draw.io 的 save 对话框 :用 file.ui.setFileData() 而不是 file.setData(),避免 Yjs 已经处理持久化的情况下还弹出「Save diagrams to:」
  • TypeScript 全链路:从 binding 到 transform 到 bridge,全部是类型安全的

快速体验

bash 复制代码
git clone https://github.com/mizuka-wu/y-mxgraph.git
cd y-mxgraph
pnpm install
pnpm --filter y-mxgraph build

# 单页模式
pnpm --filter @y-mxgraph/demo dev

# iframe 模式(打开 http://localhost:5173/iframe.html)

开两个浏览器窗口,进同一个 room,实时协作效果立即可见。


适合谁用

  • 已经在用 draw.io 的团队,想加实时协作但不想换工具
  • 做低代码/流程编排平台的开发者,需要内嵌可协作的图形编辑器
  • 需要在 iframe 里安全 embed draw.io 的 SaaS 产品

y-mxgraph 不是又一个白板工具,它是「让 draw.io 长出协作能力」的 adapter。如果你的技术栈里已经有 draw.io 和 Yjs,这个库能让它们连在一起。


标签

Yjs draw.io 实时协作 iframe 开源 前端工具库

相关推荐
其实防守也摸鱼2 小时前
软件安全与漏洞--软件安全编码
java·前端·网络·安全·网络安全·web·工具
发现一只大呆瓜3 小时前
Vite 开发预构建机制详解,搞懂 esbuild 与 Rollup 分工差异
前端·面试·vite
阿正的梦工坊4 小时前
【Typescript】14-高级实战-设计类型安全的-api
typescript
九九落4 小时前
前端获取经纬度完全指南:从Geolocation API到地图集成
前端·获取经纬度
来恩10034 小时前
jQuery选择器
前端·javascript·jquery
前端繁华如梦4 小时前
树上挂苹果还是挂玻璃球?Three.js 程序化果实的完整实现指南
前端·javascript
墨痕诉清风5 小时前
Web浏览器客户端检测网站网络健康(代码)
前端·网络·测试工具
IMPYLH5 小时前
Linux 的 wc 命令
linux·运维·服务器·前端·bash
happybasic5 小时前
Python库升级标准流程~
linux·前端·python