多绘图库插件工作台:工具适配器、路由状态与草稿自动保存
本文目录
本文审阅的是个人绘图插件的本机工作区;本机 HEAD 与 GitHub 默认分支当前同为提交 SHA 711767d,但本机另有未提交源码修改和新增测试。下文关于目录与适配入口的描述对应该提交;自动保存状态、只读切换、图形拖拽提交和新增测试等内容明确来自本机未提交工作区,不能视为默认分支已有能力。源码目录包含 Fabric、React Three Fiber、X6 和 Excalidraw 等适配入口,UI 与服务侧也有独立构建目标。文章不公开仓库标识、私有包或配置,也不复制项目源码。目录和依赖只能证明入口存在,不能证明每个工具均已在实际宿主运行通过。
外壳负责生命周期,适配器负责引擎
各绘图库的节点、相机、撤销、序列化与卸载方式并不相同。若都放在一个组件中,条件分支会随着工具数量不断增长。外壳更适合管理工具选择、布局、文档身份和保存状态;每个 adapter 负责初始化引擎、加载自己的版本化状态、序列化以及完全释放资源。
type DrawingAdapter<TState> = {
mount(container: HTMLElement, initial: TState): Promise<void>;
serialize(): Promise<TState>;
dispose(): void;
};
接口要小,不能为追求“通用格式”抹掉引擎能力差异。外层可记录工具 ID、schema 版本、文档 ID 和更新时间,具体图层与节点状态由适配器维护。遇到旧版本或损坏文档时,应保留原始内容供恢复,而不是静默覆盖为空白画布。
URL 只承载轻量选择
源码中有按 URL 参数解析绘图工具的函数,便于直达或分享一个工具入口。URL 适合放工具类型、非敏感演示编号等短状态;访问令牌、私有文档和完整绘图 JSON 不应进 query string,因为它们可能进入历史记录、日志和分享文本。解析器必须使用白名单、限制长度,并为未知工具提供确定的安全回退。
本机未提交版本的 hook 会读取并解码嵌入数据,每次读取使用递增序号,较早请求的迟到结果不会覆盖较新的读取;保存侧以 500ms 防抖合并输入、用 Promise 队列串行写入,并在宿主持久化调用成功后显示“已保存”。失败数据保留在待提交状态,组件仍挂载且可编辑时可重试。这些实现来自未提交代码审阅,不属于 GitHub 默认分支;它们能处理常见的请求乱序和短时写入失败,但不是带文档版本号的事务。
本机未提交改动与验证范围
本机工作区的 useEmbedData 将保存延迟设为 500ms。组件 effect 清理时会先清除计时器,再调用内部 flushPendingSave;该清理函数以 void 启动异步 flush,并不等待宿主持久化 API 完成。若写入在卸载后失败,payload 虽会暂存在即将销毁的 hook 实例中,但用户已看不到重试入口;整页离开时,浏览器也不保证任意异步 API 一定完成。定向测试确认卸载会触发待保存数据的写入调用,但 mock 测试不证明真实宿主一定完成落盘。这是依据本机未提交源码控制流识别的边界,不是宿主环境里的丢稿复现,也不能证明平台没有其他保存机制。更稳妥的改进是由可等待的文档切换流程显式 flush,并在成功前阻止切换或提供可恢复草稿;不要依赖 pagehide 作为可靠保存机制。
本机未提交的只读状态 hook 默认不可编辑,权限检查失败时也保持只读;工具路由会在工具类型或读写权限改变时重新挂载编辑器。Fabric 绘制在指针释放时提交最终几何,箭头完成后合并为一个组;React Three Fiber 在变换拖拽结束时另行提交最终坐标;X6 收到宿主新数据时静默刷新画布并清空撤销历史。这些是本机工作区的代码观察,当前新增测试没有覆盖 R3F、X6 与真实宿主间的集成。
新增的 9 项定向源码测试全部通过:覆盖画布状态与重试按钮、折叠按钮可访问性、Fabric 线段/箭头/矩形的最终拖拽保存,以及 hook 的卸载 flush、写入顺序和失败重试。npm run lint 与 TypeScript no-emit 检查也通过。没有安装依赖、执行 Vite 构建、运行嵌入宿主或测量资源,因此不能据此宣称插件已完成集成验收。
本机 hook 还订阅宿主的数据变更事件,卸载时通过 active 标记阻止迟到回调继续触发读取,但本层没有显式注销监听。复核到的宿主桥接类型声明中,on(event, handler) 返回 void,且没有声明 off、removeListener 或 unsubscribe 句柄;这只能说明当前类型接口没有暴露可调用的解绑方式,不能断言宿主销毁嵌入应用时发生监听器泄漏。真实集成仍需检查宿主销毁时的监听器回收行为,或由宿主补充可撤销的订阅协议。
Data URL 的边界
Data URL 很适合小型离线示例,因为它易于封装和演示;大图或持续创作内容则会遭遇编码膨胀、地址长度、内存和隐私暴露问题。应为 payload 设字节上限,对文档 schema 做运行时校验;公开 Demo 与个人私有草稿使用不同存储和授权路径。若需要账号同步,服务端必须做对象级授权,不能相信前端提交的 document ID。
建议让用户看得见“本地草稿、同步中、已同步、保存失败”等状态。多版本数据应定义迁移策略,保存失败时提供重试或下载原始草稿。关闭页面时可以尝试 flush,但浏览器不保证任意异步保存都能完成,因此自动保存不等于可靠备份。
切换引擎时释放资源
卸载 React 组件不会替第三方引擎自动清理一切。adapter 的 dispose 应移除窗口监听、观察器和计时器,释放 WebGL renderer、纹理与几何体,撤销临时 Blob URL,并取消过期保存任务。3D 视图还需评估设备丢失、显存、设备像素比和长时间切换后的资源增长。
若示例通过 iframe 执行外部内容,应使用来源白名单、最小 sandbox 权限和严格 CSP;只有确实需要通信时才建立版本化 postMessage 协议,并校验 origin、source、schema 与消息大小。当前绘图目录审阅不等于宿主安全审计,更不证明可以安全地将插件原样托管到博客。
适配器验收矩阵
- 列出每个工具的导入/导出、撤销、只读、缩放、键盘和移动端能力。
- 用旧 schema、损坏内容、超限 payload 与未知工具 ID 测试恢复策略。
- 覆盖防抖、flush、失败重试、离页、切文档和乱序响应。
- 连续打开/关闭各引擎,检查监听器、WebGL 资源、URL、计时器与内存回落。
- 检查 URL 参数、跨窗口通信、CSP、导入文件和公开/私有权限隔离。
- UI 与服务目标分别构建,并覆盖声明支持的桌面、移动浏览器及低性能设备。
本次复核确认了默认分支提交中的绘图适配器目录与工具入口,并单独审阅了本机未提交的自动保存、权限切换和绘制提交改动。新增的 9 项定向测试全部通过,但未覆盖 R3F/X6 宿主集成;没有安装依赖、执行构建、运行嵌入宿主或测量资源。宿主桥接类型没有提供本层可调用的解绑签名,运行时是否统一回收监听器仍未验证;适配协议与安全/可靠性清单属于建议,不是对项目当前能力的认证。
参考:Fabric.js、React Three Fiber、React 管理状态、MDN iframe sandbox。
Live Notes
留言提交后会先进入审核,审核通过后对其他读者公开。