TinyBase 持久化入门:Persister 框架与自动存取 Store 数据的完整指南
数据库后端前端【免费下载链接】tinybaseA reactive data store sync engine.项目地址https://gitcode.com/gh_mirrors/ti/tinybase点击查看免费下载TinyBase 的 Store 是纯内存数据结构数据默认不会在页面刷新、应用重启后保留。本篇围绕 TinyBase 官方文档《An Intro To Persistence》展开系统讲解 persister 持久化框架的全部 Persisters 分类基础型、数据库型、Durable Object 型、第三方 CRDT 型、save/load显式持久化操作、startAutoPersisting等自动存取机制并结合开源仓库源码剖析其底层的状态机、竞态防护与调度实现帮助你为本地优先local-first应用选择合适的持久化方案并正确接入。为什么需要 PersisterStore 是纯内存的TinyBase 的 Store 本质上是一个内存中的响应式数据结构reactive in-memory data store它的全部状态只存在于当前运行时的堆内存中。这意味着浏览器刷新或关闭后Store 数据丢失服务器进程重启后Store 数据丢失客户端与服务端之间无法通过 Store 直接共享状态。Persister 模块框架persisters module framework就是为解决这个问题而设计的它让你把 Store 的数据保存save到某个位置或从某个位置加载load回来。文档中列举的典型使用场景包括在浏览器会话之间或刷新之间保留 Store 数据把浏览器状态保存/加载到服务端或从服务端取回在有文件系统访问权限的环境中如 Node.js把 Store 数据保存到磁盘自 v4.0 起支持持久化到 SQLite、PostgreSQL或绑定到 Yjs、Automerge 等 CRDT 框架。所有 Persister 都构建在同一个通用核心之上。从源码结构看每个具体的 Persister 实现浏览器存储、文件、SQLite、PostgreSQL……最终都调用 createCustomPersister向其注入四个核心回调回调职责getPersisted从底层存储读取持久化内容setPersisted把内容或变更写入底层存储addPersisterListener注册“存储端发生变化”的监听用于自动加载delPersisterListener移除上述监听框架层则统一负责状态管理、竞态防护、操作调度、自动保存/加载生命周期——这正是所有 Persister 行为一致的原因。Persister 分类总览各 Persister 以**独立安装的模块entry point**形式提供每个模块返回不同种类的 Persister。合起来它们覆盖本地、远程、数据库、跨同步边界的 CRDT 场景。以下完整继承自官方文档的分类与清单。基础型 PersisterBasic Persisters这类 Persister 相对简单通常加载/保存 Store 的JSON 序列化版本适合较小数据集或需要在基础浏览器/服务器环境中落地数据的场景。Persister存储位置SessionPersister浏览器 session storageLocalPersister浏览器 local storageOpfsPersister浏览器 origin private file systemOPFSFilePersister本地文件IndexedDbPersister浏览器 IndexedDBRemotePersister远程服务器ReactNativeMmkvPersisterReact Native 中的 MMKV以 createSessionPersister / createLocalPersister 为例两者只是把不同的Storage对象sessionStorage/localStorage传给同一个createStoragePersister工厂序列化使用jsonStringWithUndefined/jsonParseWithUndefined见 common/json.ts。而 createOpfsPersister 在写入时采用“写成功后close、写失败则abort”的可写流事务语义这一点在单元测试 browser.test.ts 中被明确验证closes successful OPFS writes/aborts failed OPFS writes。createFilePersister 展示了文件型持久化的工程细节保存时先写入一个带唯一 ID 的临时文件chmod保留原文件权限位后通过rename原子替换目标文件失败则回滚并unlink临时文件自动加载则通过watchFile50ms 轮询间隔监听外部修改并对比lastContent以避免把自身写入误判为外部变更。数据库型 PersisterDatabase Persisters这类 Persister 可以把 Store 以JSON 序列化或表结构tabular两种形态加载/保存到数据库适合更大数据集通常运行在服务端——但当浏览器中存在 SQLite 实例时同样可用。Persister存储位置Sqlite3PersisterNode 中的 SQLiteBetterSqlite3PersisterNode 中的 SQLitebetter-sqlite3SqliteBunPersisterBun 中的 SQLitebun:sqliteSqliteWasmPersister浏览器中的 SQLitesqlite-wasmExpoSqlitePersisterReact Native 中的 SQLiteexpo-sqliteReactNativeSqlitePersisterReact Native 中的 SQLitereact-native-sqlite-storageCapacitorSqlitePersisterCapacitor 中的 SQLitecapacitor-sqliteCrSqliteWasmPersisterSQLite CRDTcr-sqlite-wasmElectricSqlPersisterElectric SQLLibSqlPersisterTurso 的 LibSQLlibsql-clientPowerSyncPersisterPowerSyncpowersync-sdkPgPersisterPostgreSQLpgPostgresPersisterPostgreSQLpostgresPglitePersisterPostgreSQLPGliteSupabasePersisterSupabasesupabase-js从类型定义 DatabasePersisterConfig 可以看到数据库型 Persister 的核心配置形态type DatabasePersisterConfig DpcJson | DpcTabular; // JSON 模式整个 Store 存为一行 JSON { mode: json; storeTableName?: string; // 存放 Store 的表名 storeIdColumnName?: string; // id 列名 storeColumnName?: string; // 存放 JSON 的列名 autoLoadIntervalSeconds?: number; // 自动加载轮询间隔 } // 表结构模式每个 Table 映射到数据库的一张表 { mode: tabular; tables?: { load?: {[tableName: string]: { tableId, rowIdColumnName?, condition? } | Id}; save?: {[tableId: string]: { tableName, rowIdColumnName?, deleteEmptyColumns?, deleteEmptyTable?, condition? } | string}; }; values?: { load?, save?, tableName? }; // Values 的持久化配置 autoLoadIntervalSeconds?: number; }tabular模式允许为每个 TinyBase Table 指定独立的数据库表、行 ID 列和过滤condition并可精细控制是否删除空列/空表。数据库型 Persister 的共同基座是 createCustomSqlitePersister / createCustomPostgreSqlPersister二者额外接受onSqlCommand回调用于记录底层执行的 SQL 语句。Durable Object Persister专为 Cloudflare Durable Objects一种专用的服务端存储单元设计。配合WsServerDurableObject使用既能同步多个客户端又能把数据持久化到 Durable Object 的 key-value 存储或 SQLite 存储中。Persister存储DurableObjectStoragePersisterCloudflare Durable Object 的 key-value 存储DurableObjectSqlStoragePersisterCloudflare Durable Object 的 SQLite 存储第三方 CRDT 与 Socket Persister这类 Persister 把 Store 绑定到第三方 CRDT 框架或经 socket 与 PartyKit 同步Persister存储YjsPersisterYjs CRDTyjsAutomergePersisterAutomerge CRDTautomerge-repoPartyKitPersisterPartyKitpersister-partykit-server 模块此外你还可以开发完全自定义的 Persister——这正是createCustomPersister公开 API 的用武之地源码中 persister/index.ts 入口 即直接导出了common/create.ts与两个数据库通用工厂。Persister 显式操作save 与 loadPersister 让你显式地保存或加载数据save()方法负责保存load()方法负责加载。由于底层存储可能本身就是异步的两个方法都返回 Promise因此应当使用await保证后续代码的执行顺序。以“把数据保存到浏览器 session storage 并读回”为例原文档示例完整保留import {createStore} from tinybase; import {createSessionPersister} from tinybase/persisters/persister-browser; const store createStore() .setValues({employees: 3}) .setTables({pets: {fido: {species: dog}}}); const persister createSessionPersister(store, petStore); await persister.save(); console.log(sessionStorage.getItem(petStore)); // - [{pets:{fido:{species:dog}}},{employees:3}] sessionStorage.setItem( petStore, [{pets:{toto:{species:dog}}},{employees:4}], ); await persister.load(); console.log(store.getTables()); // - {pets: {toto: {species: dog}}} console.log(store.getValues()); // - {employees: 4} sessionStorage.clear();几个值得注意的实现细节持久化内容是一个二元组[tables, values]的 JSON 数组——这正是sessionStorage中看到的[{pets:...},{employees:3}]结构load()接受可选的initialContent参数load(initialContent?: Content | (() Content))见 Persister 接口。当存储层为空时用它注入默认数据从源码看load()内部会先检查status ! StatusValues.Savingcreate.ts即正在保存时不会启动加载解析失败例如存储内容损坏时错误被交给onIgnoredError处理若提供了initialContent则回退到默认内容save()对称地检查status ! StatusValues.Loading并会经过schedule()串行执行见下文竞态防护。自动加载与自动保存当不想手动管理每一次持久化时Persister 对象还提供了自动保存与自动加载自动保存Automatic saving监听 Store 的变更一旦事务完成就立即持久化数据。从源码看startAutoSave先执行一次save()然后注册addDidFinishTransactionListener事务完成监听器每次事务结束后调用getChanges()获取增量变更只有hasChanges判定确有变更时才调用save(changes)create.ts。把 changes 传给setPersisted意味着数据库型 Persister 可以做增量写入而非整库重写自动加载Automatic loading监听或轮询持久化数据的变化并把变化反映到 Store。以浏览器存储为例createStoragePersister 注册的是window上的storage事件监听——另一个标签页修改了同一 key本标签页的 Store 会自动同步这也就是文档中StorageEvent注释的来源而文件型 Persister 则用watchFile轮询。启动与停止的 API 一览方法作用startAutoLoad(initialContent?)启动自动加载initialContent作为存储层为空时的默认 TablesstartAutoSave()启动自动保存startAutoPersisting(initialContent?, startSaveFirst?)便捷包装一条命令同时启动自动保存与加载stopAutoLoad()/stopAutoSave()分别停止stopAutoPersisting(stopSaveFirst?)同时停止两者destroy()停止全部自动行为并释放监听等资源这些方法都是异步的因为启动时会先做一次立即的 save 和 load再开始监听后续变更。完整示例原文档示例保留await persister.startAutoPersisting([{pets: {fido: {species: dog}}}, {}]); store.delValues().setTables({pets: {felix: {species: cat}}}); // ... console.log(sessionStorage.getItem(petStore)); // - [{pets:{felix:{species:cat}}},{}] sessionStorage.setItem(petStore, [{pets:{toto:{species:dog}}},{}]); // - StorageEvent(storage, {storageArea: sessionStorage, key: petStore}) // ... console.log(store.getTables()); // - {pets: {toto: {species: dog}}} await persister.destroy(); sessionStorage.clear();startAutoLoad的initialContent参数特别适用于应用首次运行、持久化层还是空的场景Store 会被实例化为一份合理的初始数据。另外startAutoPersisting的第二参数startSaveFirst允许你指定先启动保存还是先启动加载源码中通过 generation 计数器 保证中途失败时能正确回滚已启动的那一半。竞态防护与调试一个必须了解的注意事项很多场景下你会同时开启自动保存和自动加载让数据持续保持同步例如在浏览器标签页之间保持状态一致。框架对此做了一些基础防护状态机互斥内部维护Idle / Loading / Saving三态Status 枚举。load()在Saving状态下直接跳过save()在Loading状态下直接跳过——即“正在加载时不会尝试保存反之亦然”串行调度schedule()方法把可能产生竞态的操作load、save、自动加载回调中的写回排入同一个按scheduleId索引的动作队列逐个顺序执行create.ts避免并发写入交错。但要清楚这只是“基础防护”。你应当全面测试自己的持久化策略评估数据丢失的可能性——典型场景是弱网条件下向服务端保存数据失败。为此框架提供了两个调试钩子自 v4.0.4 起onIgnoredError所有 Persister 的create方法都接受该可选参数。Persister 在保存/加载过程中本会静默忽略的错误例如解析到损坏的存储数据会回调到这里。官方建议在开发环境使用它来排查持久化问题。测试文件 browser.test.ts 中的reports malformed storage events用例演示了这一点向 storage 塞入非法 JSON{触发 storage 事件后onIgnoredError会被调用onSqlCommand基于数据库的 Persister 额外接受该可选参数用于记录向底层数据库发出的每一条命令/查询便于排查数据库层面的问题。此外Persister 还暴露了getStatus()/addStatusListener()用于观测 Idle/Loading/Saving 状态变化以及getStats()返回{loads, saves}计数PersisterStats都可以在调试持久化行为时派上用场。小结使用 persisters 模块可以在各类常见持久化层浏览器存储、本地文件、IndexedDB、远程服务器、SQLite、PostgreSQL、CRDT 框架等之间加载和保存 Store 数据当内置方案不满足需求时基于公开的createCustomPersister可以开发自己的 Persister显式save/load适合精确控制的场景startAutoPersisting适合“常开常同步”的本地优先应用无论选择哪种策略都应在弱网、崩溃恢复、多标签页并发等边界情况下充分测试并用onIgnoredError开发期与onSqlCommand数据库型作为调试手段。下一步可以阅读同系列的 数据库持久化指南SQLite 表结构持久化、第三方 CRDT 持久化指南Yjs / Automerge 同步与自定义持久化指南以及仓库中 test/unit/persisters 目录下各 Persister 的单元测试深入查看每种存储后端的实际行为。赞分享数据库后端前端【免费下载链接】tinybaseA reactive data store sync engine.项目地址https://gitcode.com/gh_mirrors/ti/tinybase点击查看免费下载相关推荐TinyBase 架构选型指南Store、持久化、同步与 create-tinybase 脚手架全解析TinyBase 架构选型指南Store、持久化、同步与 create tinybase 脚手架全解析 TinyBase 是一个面向 local first数据库后端前端TinyBase 数据持久化完全指南从浏览器存储到数据库TinyBase 数据持久化完全指南从浏览器存储到数据库 想要让你的应用数据在页面刷新后依然存在TinyBase提供了完整的数据持久化解决方案让你能够轻松数据库后端前端Fantastic-admin 框架 Pinia Store 开发指南模板、持久化与自动导入实战Fantastic admin 框架 Pinia Store 开发指南模板、持久化与自动导入实战 本篇指南围绕 Fantastic admin 管理系统中 P前端AI 技能创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考