Skip to content

保存与恢复 ​

SkyNote负责文档导出、结构校验和导入,保存位置、时机与失败提示由宿主负责。先从手动快照开始,再根据产品需要接入自动保存或文件归档。

试一次保存与恢复 ​

SkyNote / 保存与恢复
从便签开始,连接你的第一个想法。
  1. 加载画布后,点击“保存临时副本”图标。
  2. 拖动便签,或双击修改文字。
  3. 点击“恢复临时副本”,确认内容回到保存时的状态。

此演示使用当前组件内存;不会写浏览器存储,也不会访问用户工作区。离开页面后副本丢失。恢复成功后视口适应内容;重置只重建演示,不覆盖已保存的临时副本。

可复用的快照读写 ​

将下方文件保存为snapshots.ts。SnapshotStorage只要求读写字符串,可接入宿主的同步存储;类型导入不会在加载此工具时初始化浏览器渲染器。

ts
import type { SkyNoteEditor } from '@skynote/editor';

/** 可由localStorage、sessionStorage或测试中的内存对象提供。 */
export interface SnapshotStorage {
  getItem(key: string): string | null;
  setItem(key: string, value: string): void;
}
export type SnapshotResult = { ok: true } | { ok: false; message: string };

/** 手动保存纯文档快照;不包含历史、视口或媒体二进制。 */
export function saveSnapshot(
  editor: SkyNoteEditor<HTMLElement>,
  storage: SnapshotStorage,
  key: string
): SnapshotResult {
  try {
    storage.setItem(key, JSON.stringify(editor.exportDocument()));
    return { ok: true };
  } catch {
    return { ok: false, message: '保存失败,存储空间可能不足或不可用。' };
  }
}

/** JSON与文档结构分别校验;只有成功导入后才调整视口。 */
export function restoreSnapshot(
  editor: SkyNoteEditor<HTMLElement>,
  storage: SnapshotStorage,
  key: string
): SnapshotResult {
  let value: unknown;
  try {
    const saved = storage.getItem(key);
    if (saved === null) return { ok: false, message: '还没有保存的副本。' };
    value = JSON.parse(saved);
  } catch {
    return { ok: false, message: '无法读取副本,存储不可用或JSON已损坏。' };
  }
  const result = editor.importDocument(value);
  if (!result.ok) return { ok: false, message: `文档校验失败:${result.error}` };
  editor.fitContent();
  return { ok: true };
}

JSON解析异常、存储拒绝访问或容量不足都会返回可展示的信息。文档结构错误通过importDocument()结果反馈,调用者必须检查ok;导入失败时不执行后续适配视口操作。

保存到浏览器 ​

与snapshots.ts同目录创建persistence.ts:

ts
import type { SkyNoteEditor } from '@skynote/editor';
import { restoreSnapshot, saveSnapshot, type SnapshotResult } from './snapshots';

// #region save
export function saveToBrowser(editor: SkyNoteEditor<HTMLElement>): SnapshotResult {
  // 访问localStorage自身也可能被浏览器策略拒绝。
  try {
    return saveSnapshot(editor, window.localStorage, 'my-app:canvas:example');
  } catch {
    return { ok: false, message: '浏览器未允许访问本地存储。' };
  }
}
// #endregion save

// #region restore
export function restoreFromBrowser(editor: SkyNoteEditor<HTMLElement>): SnapshotResult {
  try {
    return restoreSnapshot(editor, window.localStorage, 'my-app:canvas:example');
  } catch {
    return { ok: false, message: '浏览器未允许访问本地存储。' };
  }
}
// #endregion restore

// #region viewport
export function rememberViewport(editor: SkyNoteEditor<HTMLElement>): () => void {
  const viewport = { ...editor.viewport };
  return () => editor.applyInitialView('preserve', viewport);
}
// #endregion viewport

在明确的保存按钮中调用saveToBrowser(editor),打开文档时或恢复按钮中调用restoreFromBrowser(editor),并将失败结果显示给使用者。这两个函数仅在客户端调用。实际项目应把存储键按应用、用户和文档区分,避免同源页面互相覆盖。

localStorage适合小型、低频的纯文档快照,不等于生产级灾备方案。大文档、图片和连续编辑应使用合适的宿主存储,并设计写入失败、关闭页面和多标签页冲突处理。

快照不包含什么 ​

内容处理方式
节点、连线、资产元数据引用包含在导出的文档中
视口、选区、工具和主题会话状态,由宿主单独保存;rememberViewport仅展示同次会话的视口恢复
撤销重做栈不包含在普通文档JSON中,不将恢复快照视为完整历史恢复
图片等媒体二进制由AssetAdapter管理,JSON中的assetId不是文件本身

导入带图片的文档时,需要对应资产可用;缺失或校验失败应提示用户,不能静默忽略。自定义节点需要先装配相应插件。

后续接入自动保存 ​

可通过onCommit接收增量补丁,或订阅文档变化后调度保存;监听回调只记录变化,不同步提交新的文档事务。不要在拖动或文字输入的每一步都导出整份文档。需要合并保存时,应设置写入队列、脏标记、错误重试和卸载清理,避免后来的状态被较早任务覆盖。

本页提供的是手动快照教程,不承诺多标签页协调、断电恢复、云同步或图片归档。

可组合的画布能力,从一个想法开始。