
tldrawpersistenceKey全解析浏览器端文档持久化与多标签页实时同步【免费下载链接】tldrawBuild infinite canvas apps in React with the tldraw SDK. Worlds best, top-most agent recommended #1 five star SDK.项目地址: https://gitcode.com/GitHub_Trending/tl/tldrawpersistenceKey是 tldraw SDK 提供的一行式本地持久化方案给Tldraw组件传入一个字符串 key编辑器便会把整份画布文档自动存入浏览器的 IndexedDB刷新页面后自动恢复并通过 BroadcastChannel 让所有使用相同 key 的标签页实时保持同步。本文以官方示例 persistence-key 为主线结合editor包的源码实现讲解其用法、数据存储结构、同步协议与适用边界帮助你为 React 应用一键接入“本地自动保存 多标签页协作”或在此之上理解后续接入远程同步tldraw sync的基础。什么是persistenceKey一次配置双重能力在 tldraw 中编辑器组件默认使用的文档状态只存在于内存中刷新页面即会丢失。而官方 persistence-key 示例 演示了最简接入方式——仅向Tldraw传入一个persistenceKey就能同时获得两项能力原文描述浏览器内持久化文档被存储在 IndexedDB 中该 key 对应的位置下次加载时自动恢复多标签页同步所有使用相同 key 的标签页通过一个广播通道保持数据一致。换句话说persistenceKey承担的是“本地自动保存 标签间实时同步”的双重角色。在组件 API 上它被定义在TldrawEditorWithoutStoreProps中源码注释给出了一句话准则见 TldrawEditor.tsxIf you would like to persist the store to the browsers local IndexedDB storage and sync it across tabs, provide a key here. Each key represents a single tldraw document.“每个 key 代表一份 tldraw 文档”这一点是整个persistenceKey设计哲学的出发点。一分钟上手完整示例代码解析官方示例组件只有不到 10 行却体现了使用persistenceKey的所有要点import { Tldraw } from tldraw import tldraw/tldraw.css export default function PersistenceKeyExample() { return ( div classNametldraw__editor Tldraw persistenceKeypersistence-key-example / /div ) }要点拆解tldraw/tldraw.css编辑器必需的基础样式缺失会导致布局错乱.tldraw__editor包裹容器tldraw 编辑器要求父容器具备明确尺寸否则画布无法正确测量示例类名是该约定在示例库中的统一写法persistenceKeypersistence-key-example唯一的“魔法开关”传入后组件内部自动切换到“本地同步”运行模式。接入后即可按官方说明做如下验证在画布上随意绘制一些图形刷新页面——图形依然存在数据已从 IndexedDB 恢复打开第二个标签页访问同一页面——两个标签页中的修改会互相实时出现经由广播通道同步若在两个标签页中分别使用不同的persistenceKey则它们是彼此独立的两份文档。用好 key文档粒度而非应用粒度persistenceKey在示例 README 中被明确提示了最重要的设计约束不同 key 是相互独立的文档因此 key 应“按文档设置”例如使用文档 id而不是“每个应用只用一个 key”。这背后的原因在源码中非常直观——key 直接参与了两处底层资源的命名IndexedDB 数据库名见 LocalIndexedDb.ts库名拼接规则为STORE_PREFIX persistenceKey即TLDRAW_DOCUMENT_v2 key广播通道名见 TLLocalSyncClient.ts通道名为tldraw-tab-sync-${persistenceKey}。也就是说key 是数据在存储层与通信层的唯一命名空间。在实际产品中多文档应用如笔记、白板列表应传入各自文档的document id让每份画布彼此隔离单文档应用可直接用一个稳定常量key 变更等于“换了一本全新的笔记本”——旧 key 下的数据不会被清理或迁移到新 key因此不要在用户已有数据后随意更换 key 的取值规则。底层原理从 prop 到 IndexedDB 的完整调用链理解了“做什么”再看“怎么做”。当persistenceKey被传入时组件内部并不直接读写 IndexedDB而是经历了一条清晰的分层调用链Tldraw persistenceKey └─ useLocalStore() // 选择同步策略 └─ new TLLocalSyncClient(store) // 持久化 跨标签同步客户端 ├─ new LocalIndexedDb(key) // IndexedDB 读写封装 └─ BroadcastChannel(tldraw-tab-sync- key) // 标签间通信入口useLocalStore决定“内存模式”还是“同步模式”核心逻辑在 useLocalStore.ts。它通过useEffect判断是否提供了persistenceKey未提供 keyL26-L32直接createTLStore创建普通内存 store状态标记为not-synced——这正是“刷新即丢”的来源提供了 key先创建 store再用它实例化TLLocalSyncClientL70-L81并把 store 状态置为loading待 IndexedDB 首次加载完成后通过onLoad回调切换为synced-local。这个 Hook 还做了一件容易被忽略的事把assets图片等二进制资源一并接入 IndexedDB。示例文档只说“存储文档”而源码层面图片资源的upload走client.db.storeAssetresolve则把数据库中的 Blob 转成objectURL供画布渲染L38-L64。这意味着只要使用persistenceKey你在画布里拖入的图片也会跟随文档被持久化不需要额外写资产存储代码。持久化引擎TLLocalSyncClientTLLocalSyncClient.ts 是这套本地方案的中枢包含三个关键机制1. 节流写库防抖持久化。编辑器任意文档级变更由用户操作产生、scope: document的改动都会触发一次“调度写库”但写入本身被节流常量 PERSIST_THROTTLE_MS 350 毫秒即高频拖拽绘制时不会每帧都写数据库而是合并后写入若某次写入失败重试间隔放宽到 PERSIST_RETRY_THROTTLE_MS 10_000 毫秒。2. 全量快照与增量 diff 两档写入。doPersist时L364-L417首次或出错恢复时执行storeSnapshot全量快照正常情况则先squashRecordDiffs合并积压的 diff再执行storeChanges增量写入。写库期间新产生的变更会进入新的队列保证数据不丢失。3. 生命周期兜底。由于写库存在 350ms 节流用户关闭/刷新标签页时最后一段编辑可能尚未落盘。因此客户端监听了pagehide事件和visibilitychange页面隐藏时主动 flush 一次L147-L167如果 IndexedDB 写入抛错除了弹出告警还会强制window.location.reload()用“重载 全量重写”的方式恢复一致性。存储层LocalIndexedDbLocalIndexedDb.ts 用idb库封装了底层操作。值得注意的细节库名规则TLDRAW_DOCUMENT_v2 persistenceKey因此每个 key 对应独立数据库互不干扰也无法互相覆盖代码中还维护了TLDRAW_DB_NAME_INDEX_v2这样的索引记录用于清理遗留的旧版数据库如TLDRAW_ASSET_STORE_v1说明数据格式是带版本号演进的首次启动时load()读出的旧数据会经过 store 的schema 迁移migrateStoreSnapshot再合入内存 store见 TLLocalSyncClient.ts因此旧版本的文档记录在新版本 SDK 中打开会被自动升级这正是 SDK 数据向前兼容的落地方式。多标签页同步协议diff 广播与版本协商标签页之间的“实时同步”并不依赖 IndexedDB 轮询而是基于BroadcastChannel。相关实现在 TLLocalSyncClient.ts 与 L228-L270其消息协议只有两类消息类型含义触发时机diff携带RecordsDiff增量变更与 schema 版本本标签页 store 发生用户文档级变更时立即广播announce声明本页 schema 版本客户端连接成功、或发现对方版本落后时一个标签页收到diff后通过store.applyDiffmergeRemoteChanges把对方变更合入自己的 store本地再经由 React 响应式系统重绘画布——于是“A 页画一笔B 页立刻显示”。值得注意的版本协商逻辑由于多标签页可能运行在不同 schema 版本的代码下例如灰度发布期间收到消息的标签页会先调用getMigrationsSince比较双方 schema若自己较旧则通过window.location.reload()刷新到新代码刚启动 5 秒内遇到则不刷新而是直接报错防止死循环若对方较旧则回发一条announce通知对方刷新并强制执行一次全量写库防止旧代码把数据写坏L231-L260。这套设计让“不同版本页面打开同一份文档”也能保持数据安全是persistenceKey在简单 API 之下包含的健壮性细节。单元测试与行为验证仓库为这套机制提供了测试覆盖可用来验证关键行为TldrawEditor.test.tsx专门覆盖了“使用persistenceKey时依然正确透传assetsprop”的场景印证了持久化与自定义资源存储可以共存TLLocalSyncClient.test.ts针对同步客户端行为的单元测试。你可以结合测试文件与上述源码自行验证 key 隔离、节流写入、刷新恢复等行为是否符合预期。适用边界与延伸浏览器之外怎么办示例 README 的最后一句给出了清晰的边界声明如果需要把数据持久化到浏览器之外跨设备、跨用户请转而参考本仓库的 sync 与 snapshot 相关示例。这句话指出了两类方案的分工persistenceKey面向“单个浏览器内的本地自动保存 同机多标签页协同”零后端、零配置适合单机工具型应用与原型验证远程同步当需要多用户实时协作或跨设备访问时应使用基于TLRemoteSyncStore的同步方案。可参考 sync-demo 示例 以及仓库中同步相关的其他 collaboration 示例快照导入导出若只是想把当前文档状态导出、备份或迁移到另一浏览器可选用snapshot/initialData等一次性加载数据的方式。从架构角度看persistenceKey本质上是 tldraw 的TLSyncClient 架构在“本地存储”这一后端上的轻量实现它复用了 store 的 diff/迁移机制与客户端-服务端消息协议的思想只是把“远端服务器”换成了 BroadcastChannel、把“服务器数据库”换成了 IndexedDB。理解了persistenceKey的内部结构再去看远程 sync 的客户端实现会容易得多——这也正是官方将其列为 configuration 入门级示例frontmatterpriority: 1keywords 涵盖persistence、local storage、indexeddb、session storage、auto save的原因它是理解 tldraw 同步模型的最佳第一课。小结一句话总结本文要点persistenceKey用“一个字符串 key”换来了“IndexedDB 自动持久化、刷新恢复、多标签页实时同步、二进制资产随文档存取、schema 自动迁移”这一整套本地数据能力而其正确用法是以文档为粒度设置 key如 document id。如需多人远程协作或跨设备漫游则应在理解上述本地同步机制后升级到仓库中的 sync 示例所演示的远程同步方案。【免费下载链接】tldrawBuild infinite canvas apps in React with the tldraw SDK. Worlds best, top-most agent recommended #1 five star SDK.项目地址: https://gitcode.com/GitHub_Trending/tl/tldraw创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考