tldraw 编辑器快照持久化实战:getSnapshot 与 loadSnapshot 的完整用法

📅 发布时间:2026/9/8 22:23:07
tldraw 编辑器快照持久化实战:getSnapshot 与 loadSnapshot 的完整用法 tldraw 编辑器快照持久化实战getSnapshot 与 loadSnapshot 的完整用法【免费下载链接】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本文围绕 tldraw 官方示例 Save and load snapshots 展开讲解如何用getSnapshot()把编辑器内容序列化保存、用loadSnapshot()恢复、以及如何通过snapshot属性让编辑器从内置快照启动。读完后你将掌握在自研 React 应用中实现画布文档持久化localStorage、服务端、协作同步的完整方案并理解 snapshot 中document与session两部分数据的边界与底层实现。1. 示例目标与运行方式官方示例位于 SnapshotExample.tsx配套文档为 README.md。示例的核心交互是画布加载时自带内容——通过Tldraw snapshot{jsonSnapshot}传入一个内置的 snapshot.json在画布上画一些内容点击Save snapshot把当前编辑器状态序列化并存入localStorage继续修改画布点击Load snapshot把画布恢复回第 2 步保存时的样子内容 相机视角 选区。示例代码中的工具栏通过useEditor()获取编辑器实例再拿到editor.storetldraw 的数据存储层所有快照 API 都以 store 为操作对象import { getSnapshot, loadSnapshot, TLComponents, Tldraw, TldrawUiButton, TLEditorSnapshot, useEditor, } from tldraw import tldraw/tldraw.css import _jsonSnapshot from ./snapshot.json const jsonSnapshot _jsonSnapshot as any as TLEditorSnapshot function SnapshotToolbar() { const editor useEditor() const save () { const { document, session } getSnapshot(editor.store) localStorage.setItem(snapshot, JSON.stringify({ document, session })) } const load () { const snapshot localStorage.getItem(snapshot) if (!snapshot) return loadSnapshot(editor.store, JSON.parse(snapshot)) } return ( div classNametlui-menu snapshot-toolbar TldrawUiButton typenormal onClick{save}Save snapshot/TldrawUiButton TldrawUiButton typenormal onClick{load}Load snapshot/TldrawUiButton /div ) } const components: TLComponents { SharePanel: SnapshotToolbar, // 挂在分享面板位置避免遮挡画布 } export default function SnapshotExample() { return ( div classNametldraw__editor Tldraw snapshot{jsonSnapshot} components{components} / /div ) }样式见 snapshots.css其中“Saved”提示通过data-visible属性控制缩放与透明度过渡配合一个 1 秒后清除的setTimeout实现保存成功反馈。2. TLEditorSnapshotdocument 与 session 的双结构getSnapshot(editor.store)返回一个TLEditorSnapshot对象其类型定义在 TLEditorSnapshot.tsexport interface TLEditorSnapshot { document: TLStoreSnapshot // 文档数据页面、形状、资产 session: TLSessionStateSnapshot // 每用户会话状态 }两部分职责分明这正是示例文档README强调的核心点document内容数据。包含所有page、shape、asset等文档范围的记录以及一份 schema 描述记录类型的版本号与序列计数。它是“画布上有什么”。session每用户的编辑器会话状态是“这个用户此刻在怎么看画布”。从 TLSessionStateSnapshot.ts 的接口定义看session的结构为export interface TLSessionStateSnapshot { version: number currentPageId?: TLPageId isFocusMode?: boolean exportBackground?: boolean isDebugMode?: boolean isToolLocked?: boolean isGridMode?: boolean pageStates?: Array{ pageId: TLPageId camera?: { x: number; y: number; z: number } selectedShapeIds?: TLShapeId[] focusedGroupId?: TLShapeId | null } }即当前页面、各种开关专注模式、调试模式、网格、工具锁定、导出背景以及每页的相机x/y/z、选中的形状列表和聚焦的组。pageStates是“每页一份”的因此多页文档恢复后每个页面都会回到各自的相机与选区。工程建议示例源码注释 [1] 原话在多用户应用中通常应把document和session分开存储让每个用户各自保留自己的session示例为了演示简单把两者一起序列化进了localStorage。3. getSnapshot 的底层实现getSnapshot的实现只有 10 行位于 TLEditorSnapshot.tsexport function getSnapshot(store: TLStore): TLEditorSnapshot { const sessionState$ sessionStateCache.get(store, createSessionStateSnapshotSignal) const session sessionState$.get() if (!session) { throw new Error(Session state is not ready yet) } return { document: store.getStoreSnapshot(), session, } }几个值得注意的点document部分直接调用store.getStoreSnapshot()即对 store 中全部记录做浅拷贝序列化session部分来自一个响应式信号createSessionStateSnapshotSignal见 TLSessionStateSnapshot.ts它用computed从TLINSTANCE_ID记录、每页的CameraRecordType与InstancePageStateRecordType记录实时计算得出带isEqual比较以支持响应式订阅若实例状态尚未就绪getSnapshot会抛出Session state is not ready yet。也就是说不要在编辑器刚创建、store 还未初始化完成时调用它。4. loadSnapshot 的恢复逻辑与 forceOverwriteSessionStateloadSnapshot(store, snapshot, opts)的完整实现在 TLEditorSnapshot.ts其恢复流程在一个store.atomic(...)事务中完成兼容旧格式如果传入的对象带store字段说明是一个老式TLStoreSnapshot没有 document/session 之分。实现会先调用store.schema.migrateStoreSnapshot()做 schema 迁移再过滤掉非 document 范围的记录自动把它转成新的{ document, session }结构——这是从源码结构看对旧版.tldr导出的向后兼容处理先捕获“需保留”的状态在清库前先pluckPreservingValues取出当前 instance 记录中的粘性字段并缓存当前 session 快照加载 documentstore.loadStoreSnapshot(snapshot.document)——若提供 document它会先清空 store 再写入恢复粘性实例/会话状态把第 2 步捕获的值写回避免加载快照导致编辑器出现“跳变”恢复 session若快照带session调用loadSessionStateSnapshotIntoStore恢复相机、选区、开关等。关键选项是TLLoadSnapshotOptions.forceOverwriteSessionStateexport interface TLLoadSnapshotOptions { forceOverwriteSessionState?: boolean }默认情况下isDebugMode、isGridMode等会话开关不会被快照覆盖——源码注释说明这些字段被视为用户“粘性sticky”偏好而文档数据则不是。loadSessionStateSnapshotIntoStore内部会做 primary/secondary 合并见 TLSessionStateSnapshot.ts默认以现有实例状态优先仅在需要时用快照值填补只有显式传true才让快照中的开关值强制生效。此外session 快照自带版本号当前为version: 0与迁移函数未来结构变化时可平滑升级每次加载都会经过 validator 校验非法数据会被console.warn并跳过。loadSnapshot支持只传部分结构你可以只传{ document }之后再单独恢复{ session }也可以完全跳过 session示例源码注释 [2]。这让“只恢复内容、不恢复视角”成为可能。5. snapshot 属性让编辑器带内容启动Tldraw组件接受snapshot属性编辑器创建时即把快照载入 store所以示例画布一开始就有内容注释 [3]。该属性的类型与loadSnapshot一致是PartialTLEditorSnapshot | TLStoreSnapshot——既可以传完整的双结构快照也可以传旧式 store 快照底层同样走迁移与过滤逻辑。仓库中另一个使用同类型的是 TldrawImage.tsx它把快照直接渲染为 SVG 图片例如用作分享卡片snapshot属性是其必传项。示例自带的 snapshot.json 是一个约 30KB 的完整快照文件其结构可以直观印证前文的类型定义{ document: { store: { document:document: { gridSize: 10, name: , id: document:document, typeName: document }, page:page: { id: page:page, name: Page 1, index: a1, typeName: page }, asset:-2122303015: { type: image, props: { name: tldrawFile, src: data:image/png;base64,... }, typeName: asset } // …还有 shape、binding 等文档范围记录 }, schema: { schemaVersion: 2, sequences: { com.tldraw.store: 4, com.tldraw.shape: 4, /* …每类记录的序列计数 */ } } }, session: { version: 0, currentPageId: page:page, exportBackground: true, isFocusMode: false, isDebugMode: true, isToolLocked: false, isGridMode: false, pageStates: [ { pageId: page:page, camera: { x: -367.04, y: -293.85, z: 1 }, selectedShapeIds: [], focusedGroupId: null } ] } }其中schema.sequences为每个记录类型维护序列号用于后续写入时生成稳定的自增 IDstore中以typeName:id命名的键即 store 记录本身document、page、asset、shape、binding等。6. 从示例走向生产持久化方案要点保存时机示例是手动点击按钮保存。生产环境通常监听 store 变更做防抖自动保存store支持订阅或提供显式导出为.tldr文件的入口document 与 session 分存单用户应用如本地草稿可以像示例一样两者一起存localStorage多用户/协作应用应把document交给同步层tldraw 的 sync 系列包session只留在本地注意 session 的“粘性”默认值恢复用户视角与文档内容通常没问题但isDebugMode、isGridMode等开关默认不被覆盖需要时用forceOverwriteSessionState: true旧数据兼容如果你手里有旧版导出的纯 store 快照带store字段直接传给loadSnapshot或snapshot属性即可SDK 会自动迁移并剥离非文档记录就绪检查getSnapshot在 session 状态未就绪时会抛错调用应放在编辑器初始化完成后如事件回调、按钮点击中避免在渲染期间立即调用。7. 小结tldraw 的快照机制把“内容”与“会话”显式分层getSnapshot()给出{ document, session }loadSnapshot()在一个原子事务中恢复两者并保留必要的粘性状态Tldraw snapshot{...}则让应用可以从持久化的初始内容直接启动。三者都以TLStore为中心类型约束、schema 迁移与版本校验齐备可直接用于本地草稿恢复、文件导入导出或协作同步层的落地。完整可运行代码参见 SnapshotExample.tsxAPI 实现参见 TLEditorSnapshot.ts 与 TLSessionStateSnapshot.ts。【免费下载链接】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),仅供参考