)
TanStack Query 之 createSyncStoragePersister 实战指南Preact Query 离线缓存持久化【免费下载链接】query Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query导读createSyncStoragePersister是 TanStack Query 生态中用于将 Preact Query 的查询缓存同步写入浏览器localStorage/sessionStorage的持久化工具函数。本文围绕 Preact 框架页对应文档createSyncStoragePersister的完整内容展开覆盖该插件的废弃状态与替代方案、安装方式、与persistQueryClient的组合使用、写入失败重试策略含removeOldestQuery预设策略以及全部可配置项与序列化压缩扩展并结合作者所在仓库query-sync-storage-persister包的真实实现与测试帮助你掌握在 Preact 应用中落地「离线缓存、刷新不丢数据」的完整方案。说明当前仓库中 Preact 框架的插件文档采用refreplace的共享文档机制即 Preact 页面与 React 源页面共享同一份正文仅将包名由react-query替换为preact-query。本文所写的正文即该机制下 Preact 页面实际渲染的内容代码示例中的导入路径均为 Preact 版本。现状该插件已被标记为 Deprecated在写作本文时createSyncStoragePersister已经被标记为deprecated废弃并计划在下一个大版本中移除This plugin is deprecated and will be removed in the next major version. You can simply usetanstack/query-async-storage-persisterinstead.推荐的新方案是改用异步存储持久化器createAsyncStoragePersister对应文档见 Preact 框架页 createAsyncStoragePersister它基于异步存储接口实现天然适合 IndexedDB 等不阻塞 UI 的存储介质。该废弃标记同样体现在实现源码中——仓库内 query-sync-storage-persister 的 index.ts 对函数注释明确写着/** * deprecated use createAsyncStoragePersister from tanstack/query-async-storage-persister instead. */ export function createSyncStoragePersister(/* ... */)尽管如此由于同步存储简单直接、无需引入异步适配层它仍然是大量现有项目采用的方式对于想要理解同步持久化实现原理、或维护存量代码的开发者这份文档依然值得完整阅读。安装该工具以独立包的形式发布通过tanstack/query-sync-storage-persister导入。持久化编排函数persistQueryClient与配套的PersistQueryClientProvider则在 Preact 的持久化客户端包tanstack/preact-query-persist-client中该包仅是对tanstack/query-persist-client-core与 Provider 组件的再导出见 preact-query-persist-client/src/index.ts。四种主流包管理器任选其一npm install tanstack/query-sync-storage-persister tanstack/preact-query-persist-clientpnpm add tanstack/query-sync-storage-persister tanstack/preact-query-persist-clientyarn add tanstack/query-sync-storage-persister tanstack/preact-query-persist-clientbun add tanstack/query-sync-storage-persister tanstack/preact-query-persist-client注意query-sync-storage-persister是框架无关的核心包它只依赖tanstack/query-core与tanstack/query-persist-client-core因此无论使用 React Query、Preact Query、Solid Query 还是 Vue Query创建同步持久化器的方式都完全一致区别仅在persistQueryClient的导入来源Preact 场景下必须从tanstack/preact-query-persist-client导入。基本用法createSyncStoragePersister的使用分为三步导入createSyncStoragePersister函数创建一个syncStoragePersister实例把它交给persistQueryClient详细编排文档见 Preact 框架页 persistQueryClient。结合 Preact Query 的完整示例如下import { QueryClient } from tanstack/preact-query import { persistQueryClient } from tanstack/preact-query-persist-client import { createSyncStoragePersister } from tanstack/query-sync-storage-persister const queryClient new QueryClient({ defaultOptions: { queries: { // 缓存保留 24 小时配合持久化让数据跨越刷新/会话存活 gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }) // 写入 window.localStorage const localStoragePersister createSyncStoragePersister({ storage: window.localStorage, }) // 若想仅保留在当前标签页会话内可使用 sessionStorage // const sessionStoragePersister createSyncStoragePersister({ storage: window.sessionStorage }) persistQueryClient({ queryClient, persister: localStoragePersister, })要点解读gcTimev5 之前称为cacheTime决定查询数据在内存与存储中的存活时间持久化场景下通常要设置得比默认值更长例如 24 小时或Infinity否则数据很快被清理、持久化失去意义一个持久化器只绑定一个storage与一个key如需对多组查询做不同保留策略可分别创建实例storage只要符合getItem/setItem/removeItem三个方法的同步存储契约即可localStorage、sessionStorage都满足。从实现看query-sync-storage-persister/src/index.ts若传入的storage为空undefined/null例如服务端渲染场景或 Android WebView 将window.localStorage置为null的配置返回的 persister 三个方法persistClient、restoreClient、removeClient都会退化为noop空操作保证 SSR 或受限环境下不抛错、不写数据。持久化失败的重试Retries持久化写入并非总能成功典型场景是数据体积超过存储配额如localStorage通常只有几 MB 上限。此时storage.setItem会抛错。通过给持久化器提供retry函数可以优雅地处理这类错误。retry函数接收它尝试保存的persistedClient、本次error以及累计errorCount三个入参并且必须返回一个新的PersistedClient用于再次尝试持久化如果返回undefined则表示不再进行下一次尝试。其完整类型契约导出自tanstack/query-persist-client-core并经 Preact 持久化包再导出为export type PersistRetryer (props: { persistedClient: PersistedClient error: Error errorCount: number }) PersistedClient | undefined对照实现源码query-sync-storage-persister/src/index.ts可以看到重试逻辑是一个while循环先trySave一次失败后进入循环每次累加errorCount、调用retry得到新的客户端对象若存在则继续trySave直到保存成功或retry返回undefinedconst trySave (persistedClient: PersistedClient): Error | undefined { try { storage.setItem(key, serialize(persistedClient)) return } catch (error) { return error as Error } } // persistClient 内部节流包裹后 // let client persistedClient // let error trySave(client) // let errorCount 0 // while (error client) { // errorCount // client retry?.({ persistedClient: client, error, errorCount }) // if (client) { // error trySave(client) // } // }默认行为与预设策略默认不重试未提供retry时写入失败即静默放弃error存在但client在首次retry?.()返回undefined后退出循环不会反复尝试。内置的预设策略removeOldestQuery可从tanstack/preact-query-persist-client导入。它会在持久化失败时返回一个移除了最旧查询的新PersistedClient以此逐次缩小数据体积直到能成功写入或清空为止——非常适合处理存储空间不足。import { removeOldestQuery } from tanstack/preact-query-persist-client import { createSyncStoragePersister } from tanstack/query-sync-storage-persister const localStoragePersister createSyncStoragePersister({ storage: window.localStorage, retry: removeOldestQuery, })该策略的实际用法可以在仓库测试中看到query-sync-storage-persister/src/tests/storageIsFull.test.ts 中的「storage full」测试用例在throttleTime: 0的同时配置retry: removeOldestQuery用来验证存储写满时通过逐条淘汰最旧查询最终成功完成持久化的链路。API 参考createSyncStoragePersister调用该函数创建一个可与persistQueryClient配合使用的syncStoragePersistercreateSyncStoragePersister(options: CreateSyncStoragePersisterOptions)Optionsinterface CreateSyncStoragePersisterOptions { /** 用于设置与读取缓存项的存储客户端window.localStorage 或 window.sessionStorage */ storage: Storage | undefined | null /** 存储缓存时使用的 key */ key?: string /** 为避免频繁写入 * 传入毫秒数以节流throttle保存缓存到存储的操作 */ throttleTime?: number /** 如何将数据序列化后写入存储 */ serialize?: (client: PersistedClient) string /** 如何将存储中的字符串反序列化为数据 */ deserialize?: (cachedString: string) PersistedClient /** 写入失败时的重试策略 **/ retry?: PersistRetryer }默认值以下默认值在文档与实现源码query-sync-storage-persister/src/index.ts中完全一致{ key REACT_QUERY_OFFLINE_CACHE, throttleTime 1000, serialize JSON.stringify, deserialize JSON.parse, }对各默认参数补充说明参数默认值说明keyREACT_QUERY_OFFLINE_CACHE存储键名。作为框架无关的核心包即便在 Preact Query 中使用默认键名也保持历史命名不变同一域名下不同应用需显式传不同key避免互相覆盖throttleTime1000节流间隔毫秒。实现中通过tanstack/query-core的timeoutManager做节流间隔内的多次更新只保留最后一次在间隔结束后落盘避免高频查询更新打爆存储 I/OserializeJSON.stringify持久化前把PersistedClient转成字符串deserializeJSON.parse恢复时把字符串还原为PersistedClient另外需要注意Storage接口的最小契约是三个同步方法与 DOM 标准一致interface Storage { getItem: (key: string) string | null setItem: (key: string, value: string) void removeItem: (key: string) void }serialize与deserialize的扩展用法localStorage存在容量上限通常约 5 MB各浏览器实现略有差异。如果确实需要向localStorage写入更多数据可以覆写serialize/deserialize借助压缩库例如 lz-string对数据先压缩再存储、读取时再解压从而等效扩大可存储的查询数据量。以 lz-string 为例的完整 Preact 用法import { QueryClient } from tanstack/preact-query import { persistQueryClient } from tanstack/preact-query-persist-client import { createSyncStoragePersister } from tanstack/query-sync-storage-persister import { compress, decompress } from lz-string const queryClient new QueryClient({ // staleTime: Infinity 配合持久化可让数据视为永不过期优先读缓存 defaultOptions: { queries: { staleTime: Infinity } }, }) persistQueryClient({ queryClient: queryClient, persister: createSyncStoragePersister({ storage: window.localStorage, // 序列化JSON 字符串先压缩再写入 serialize: (data) compress(JSON.stringify(data)), // 反序列化读出的字符串先解压再 JSON.parse deserialize: (data) JSON.parse(decompress(data)), }), // 持久化缓存的最大存活时长这里配合 staleTime: Infinity 设为无限大 maxAge: Infinity, })这里同时展示了persistQueryClient侧的maxAge选项它控制恢复出的缓存最大可保留多久从存储时间戳起算默认与QueryClient的gcTime对齐设置为Infinity表示不做时间淘汰。底层执行流程一览将文档行为与实现源码对照一次完整的「查询更新 → 持久化」执行链路如下persistQueryClient订阅 QueryClient 的查询状态变化详细机制见 persistQueryClient 文档状态变化时把整个缓存序列化得到PersistedClient调用 persister 的persistClient该调用被throttle(throttleTime)节流节流窗口内只保留最后一次要保存的数据窗口结束后真正执行storage.setItem(key, serialize(persistedClient))若抛错则按上文重试循环调用retry如removeOldestQuery逐次缩小体积重试页面刷新/重新打开时persistQueryClient通过restoreClient内部执行deserialize(storage.getItem(key))恢复缓存必要时再触发重新验证refetch最终实现「离线可读、刷新不丢」的效果。值得强调的是节流throttle与防抖debounce的区别实现采用的是节流即保证间隔期内至少执行一次、且最后一次待存数据被保留见 index.ts 内throttle工具函数。配合storage为空时的 noop 降级整套设计保证了在同步存储不可用、写入失败、体积超限等边界条件下持久化层都不会让主流程崩溃。小结createSyncStoragePersister让 Preact Query 缓存可以同步落盘到localStorage/sessionStorage配合persistQueryClient即可实现离线缓存与跨刷新持久化它已进入废弃流程新项目建议评估使用 createAsyncStoragePersister异步、适合 IndexedDB存量代码仍可依据本文保持维护记得显式设置足够大的gcTime与key理解throttleTime、serialize/deserialize默认值及其扩展方式并在存储可能写满的场景配置retry: removeOldestQuery实现细节节流、重试循环、noop 降级、默认键名均可在仓库 packages/query-sync-storage-persister/src/index.ts 及其测试目录如 storageIsFull.test.ts中直接查证。如果想要了解更通用的离线机制与存储恢复流程可继续阅读 persistQueryClientPreact 以及 Preact 框架安装与使用文档。【免费下载链接】query Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考