译零 TransZero · TanStack Query 中文文档
主站

experimental_createQueryPersister

更新于 2026-11-02 Vue 查看英文原文 ↗

安装

该工具作为一个独立包提供,可通过 '@tanstack/query-persist-client-core' 导入。

Shell
1npm install @tanstack/query-persist-client-core

或

Shell
1pnpm add @tanstack/query-persist-client-core

或

Shell
1yarn add @tanstack/query-persist-client-core

或

Shell
1bun add @tanstack/query-persist-client-core

使用方法

  • 导入 experimental_createQueryPersister 函数
  • 创建一个新的 experimental_createQueryPersister 实例
    • 你可以向其传入任意符合 AsyncStorage 接口的 storage
  • 将该 persister 作为选项传给你的查询。这可以通过两种方式实现:将其传给 QueryClient 的 defaultOptions,或传给任意 useQuery Hook 实例。
    • 若将该 persister 作为 defaultOptions 传入,则所有查询都会被持久化到所提供的 storage 中。你还可以通过传入 filters 进一步缩小范围。与 persistClient 插件不同,此方式不会将整个查询客户端作为一个条目持久化,而是单独持久化每个查询;键值使用查询哈希(query hash)。
    • 若将该 persister 提供给单个 useQuery Hook,则仅该查询会被持久化。
  • 注意:queryClient.setQueryData() 操作不会被持久化。这意味着,若你执行了乐观更新,并在查询失效前刷新页面,则对查询数据所做的更改将会丢失。参见 https://github.com/TanStack/query/issues/6310

通过这种方式,你无需存储整个 QueryClient,而是可自主选择应用中值得持久化的数据。每个查询均按需恢复(即首次使用时)和持久化(每次 queryFn 执行后),因此无需节流。恢复查询后,staleTime 同样生效:若数据被视为 stale,则恢复后会立即重新拉取;若数据为 fresh,则 queryFn 不会运行。

从内存中垃圾回收一个查询 不会 影响其已持久化的数据。这意味着查询可在内存中保留更短时间,从而提升 内存效率。若下次再使用该查询,它将直接从持久化存储中恢复。

TypeScript
1import { QueryClient } from '@tanstack/vue-query'2import { experimental_createQueryPersister } from '@tanstack/query-persist-client-core'3 4const persister = experimental_createQueryPersister({5  storage: localStorage,6  maxAge: 12 * 60 * 60 * 1000, // 12 hours7})8 9const queryClient = new QueryClient({10  defaultOptions: {11    queries: {12      gcTime: 30 * 1000, // 30 seconds13      persister: persister.persisterFn,14    },15  },16})

调整后的默认值

createPersister 插件在技术上会包装 queryFn,因此当 queryFn 不运行时,它也不会恢复数据。由此,它在查询与网络之间充当缓存层。因此,当启用持久化器时,networkMode 默认为 'offlineFirst',以确保即使在无网络连接的情况下,也能从持久化存储中恢复数据。

其他工具函数

调用 experimental_createQueryPersister 除返回 persisterFn 外,还会返回额外的工具函数,便于你在用户代码中实现自定义功能。

persistQueryByKey(queryKey: QueryKey, queryClient: QueryClient): Promise<void>

该函数会将 Query 持久化至创建持久化器时指定的存储及键值。
该工具可与 setQueryData 配合使用,在不等待失效处理的前提下,将乐观更新结果持久化至存储。

TypeScript
1const persister = experimental_createQueryPersister({2  storage: localStorage,3  maxAge: 12 * 60 * 60 * 1000, // 12 hours4})5 6const queryClient = useQueryClient()7 8useMutation({9  mutationFn: updateTodo,10  onMutate: async (newTodo) => {11    ...12    // Optimistically update to the new value13    queryClient.setQueryData(['todos'], (old) => [...old, newTodo])14    // And persist it to storage15    persister.persistQueryByKey(['todos'], queryClient)16    ...17  },18})

retrieveQuery<T>(queryHash: string): Promise<T | undefined>

该函数尝试根据 queryHash 检索已持久化的查询。
若 query 为 expired、busted 或 malformed,则对应条目将从存储中移除,并返回 undefined。

已持久化的条目必须具备字符串类型的 queryHash、数组类型的 queryKey,以及包含有限数值型 dataUpdatedAt 的 state 对象。不符合这些要求的条目会在检索、垃圾回收、恢复及筛选删除过程中被自动移除,且不影响其他条目的处理。null 仍为合法的查询数据。

persisterGc(): Promise<void>

该函数可用于定期清理存储中状态为 expired、busted 或 malformed 的条目。

要使该函数正常工作,你的存储必须暴露 entries 方法,且该方法需返回一个 key-value tuple array。
例如 Object.entries(localStorage) 对应 localStorage,或 idb-keyval 中的 entries。

restoreQueries(queryClient: QueryClient, filters): Promise<void>

该函数可用于恢复当前由持久化器存储的查询。
例如,当应用在离线模式下启动,或你希望上一 session 中全部或特定数据能立即可用(而无需经历中间 loading 状态)时。

过滤对象支持以下属性:

  • queryKey?: QueryKey
    • 设置该属性以指定用于匹配的查询键。
  • exact?: boolean
    • 若你不希望按查询键进行包含式搜索,可传入 exact: true 选项,仅返回与所传查询键完全一致的查询。

要使该函数正常工作,你的存储必须暴露 entries 方法,且该方法需返回一个 key-value tuple array。
例如 Object.entries(localStorage) 对应 localStorage,或 idb-keyval 中的 entries。

removeQueries(filters): Promise<void>

使用 queryClient.removeQueries 时,数据仍保留在持久化器中,需单独移除。
该函数可用于移除当前由持久化器存储的查询。

过滤对象支持以下属性:

  • queryKey?: QueryKey
    • 设置该属性以指定用于匹配的查询键。
  • exact?: boolean
    • 若你不希望按查询键进行包含式搜索,可传入 exact: true 选项,仅返回与所传查询键完全一致的查询。

要使该函数正常工作,你的存储必须暴露 entries 方法,且该方法需返回一个 key-value tuple array。
例如 Object.entries(localStorage) 对应 localStorage,或 idb-keyval 中的 entries。

API

experimental_createQueryPersister

TypeScript
1experimental_createQueryPersister(options: StoragePersisterOptions)

Options

TypeScript
1export interface StoragePersisterOptions {2  /** The storage client used for setting and retrieving items from cache.3   * For SSR pass in `undefined`.4   */5  storage: AsyncStorage | Storage | undefined | null6  /**7   * How to serialize the data to storage.8   * @default `JSON.stringify`9   */10  serialize?: (persistedQuery: PersistedQuery) => string11  /**12   * How to deserialize the data from storage.13   * @default `JSON.parse`14   */15  deserialize?: (cachedString: string) => PersistedQuery16  /**17   * A unique string that can be used to forcefully invalidate existing caches,18   * if they do not share the same buster string19   */20  buster?: string21  /**22   * The max-allowed age of the cache in milliseconds.23   * If a persisted cache is found that is older than this24   * time, it will be discarded25   * @default 24 hours26   */27  maxAge?: number28  /**29   * Prefix to be used for storage key.30   * Storage key is a combination of prefix and query hash in a form of `prefix-queryHash`.31   */32  prefix?: string33  /**34   * If set to `true`, the query will refetch on successful query restoration if the data is stale.35   * If set to `false`, the query will not refetch on successful query restoration.36   * If set to `'always'`, the query will always refetch on successful query restoration.37   * Defaults to `true`.38   */39  refetchOnRestore?: boolean | 'always'40  /**41   * Filters to narrow down which Queries should be persisted.42   */43  filters?: QueryFilters44}45 46interface AsyncStorage<TStorageValue = string> {47  getItem: (key: string) => MaybePromise<TStorageValue | undefined | null>48  setItem: (key: string, value: TStorageValue) => MaybePromise<unknown>49  removeItem: (key: string) => MaybePromise<void>50  entries?: () => MaybePromise<Array<[key: string, value: TStorageValue]>>51}

默认选项为:

TypeScript
1{2  prefix = 'tanstack-query',3  maxAge = 24 * 60 * 60 * 1000,4  serialize = JSON.stringify,5  deserialize = JSON.parse,6  refetchOnRestore = true,7}

本页为 TanStack Query 文档(Vue 版)的非官方中文翻译,原文由上游团队维护,遵循 MIT 许可。 译文如有偏差,以英文原文为准。

译自上游 59d11de