Vue Query 自定义客户端(Custom Client)指南:注入 QueryClient 与自定义上下文键
【免费下载链接】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
Vue Query 通过VueQueryPlugin插件将QueryClient注入到 Vue 应用的上下文中,本指南讲解两种注入方式——直接传入QueryClientConfig配置对象由插件内部创建实例,或预先创建好QueryClient实例再传入——以及如何自定义客户端在上下文中的键名(key),从而支持多个 Vue 应用(尤其是 Vue 2 场景)在同一页面共存而互不冲突。读完本文,你将掌握自定义客户端注入、自定义上下文键以及useQueryClient()取回客户端的完整实战方案。
为什么需要自定义 Client
Vue Query 默认由VueQueryPlugin在安装时自动创建并注入一个QueryClient。但存在一些场景,默认行为无法满足需求:
- 你需要在安装插件之前创建
QueryClient,以便把它传递给其他不依赖 Vue 上下文的库(例如路由守卫、独立的工具函数、测试工具等); - 你希望为不同应用或不同模块复用、共享同一个客户端实例;
- 你需要在同一个页面中挂载多个 Vue 应用(尤其是 Vue 2),它们的客户端键名需要隔离,避免上下文冲突。
为此,VueQueryPlugin的插件选项被设计为两种形态的组合:要么传入queryClientConfig(配置对象),要么传入queryClient(实例)。从源码看,vueQueryPlugin.ts 中定义了ConfigOptions与ClientOptions两个接口,并合并为联合类型VueQueryPluginOptions:
interface ConfigOptions extends CommonOptions { queryClientConfig?: QueryClientConfig } interface ClientOptions extends CommonOptions { queryClient?: QueryClient } export type VueQueryPluginOptions = ConfigOptions | ClientOptions在 安装逻辑 中,插件会优先检查是否存在queryClient属性;存在则直接使用该实例,否则读取queryClientConfig并内部new QueryClient(clientConfig):
if ('queryClient' in options && options.queryClient) { client = options.queryClient } else { const clientConfig = 'queryClientConfig' in options ? options.queryClientConfig : undefined client = new QueryClient(clientConfig) }QueryClientConfig的定义位于 types.ts,包含queryCache、mutationCache与defaultOptions三个字段:
export interface QueryClientConfig { queryCache?: QueryCache mutationCache?: MutationCache defaultOptions?: DefaultOptions }其中defaultOptions又分为queries、mutations、hydrate、dehydrate四组默认配置,详见 DefaultOptions 定义。
方式一:通过 queryClientConfig 自动创建
当你只需要对默认客户端做配置、无需在插件外部提前持有实例时,直接传入queryClientConfig即可。插件会在安装过程中内部创建QueryClient并提供给 Vue 上下文:
const vueQueryPluginOptions: VueQueryPluginOptions = { queryClientConfig: { defaultOptions: { queries: { staleTime: 3600 } }, }, } app.use(VueQueryPlugin, vueQueryPluginOptions)上面的示例将全局查询的默认staleTime设置为 3600 毫秒,此后所有通过useQuery创建的查询在 3.6 秒内都会被判定为新鲜(fresh),不会在重复挂载时立刻重新请求。
方式二:预先创建 QueryClient 实例
当需要把客户端与 Vue 上下文解耦、供其他库或模块提前使用时,先手动创建实例,再以queryClient字段传入:
const myClient = new QueryClient(queryClientConfig) const vueQueryPluginOptions: VueQueryPluginOptions = { queryClient: myClient, } app.use(VueQueryPlugin, vueQueryPluginOptions)myClient既可以通过new QueryClient()从零创建,也可以基于某个已有的配置对象(queryClientConfig)创建。注意两种选项是互斥的:queryClientConfig与queryClient同时提供时,从 安装逻辑 可以推断,插件会优先采用queryClient实例。
客户端生命周期管理
从 vueQueryPlugin.ts 可以看出,插件安装时还会负责客户端的生命周期:
- 非服务端环境下会调用
client.mount()启动客户端; - 支持
clientPersister持久化回调(恢复中置isRestoring,完成后再触发clientPersisterOnSuccess); - 应用卸载(
app.unmount或app.onUnmount)时统一执行client.unmount()与持久化卸载清理。
自定义上下文键(Custom Context Key)
QueryClient默认被注入到 Vue 上下文键VUE_QUERY_CLIENT下。你可以通过queryClientKey选项自定义键名后缀,这在同一页面同时运行多个 Vue 应用(尤其是 Vue 2)时非常有用,可以避免多个应用之间的上下文命名冲突。
只传键名、使用默认创建的客户端:
const vueQueryPluginOptions: VueQueryPluginOptions = { queryClientKey: 'Foo', } app.use(VueQueryPlugin, vueQueryPluginOptions)同时传自定义客户端与自定义键名:
const myClient = new QueryClient(queryClientConfig) const vueQueryPluginOptions: VueQueryPluginOptions = { queryClient: myClient, queryClientKey: 'Foo', } app.use(VueQueryPlugin, vueQueryPluginOptions)键名的拼接规则
从 utils.ts 的源码可以看到键名拼接的具体实现:
export const VUE_QUERY_CLIENT = 'VUE_QUERY_CLIENT' export function getClientKey(key?: string) { const suffix = key ? `:${key}` : '' return `${VUE_QUERY_CLIENT}${suffix}` }自定义键会以后缀的形式与默认键组合,无需使用者手动处理。例如传入queryClientKey: 'Foo',实际注入的上下文键为:
const vueQueryPluginOptions: VueQueryPluginOptions = { queryClientKey: 'Foo', } app.use(VueQueryPlugin, vueQueryPluginOptions) // -> VUE_QUERY_CLIENT:Foo使用自定义键取回客户端
要让使用自定义键的应用能正确取回对应的客户端,需要在查询选项中同步提供queryClientKey:
useQuery({ queryKey: ['query1'], queryFn: fetcher, queryClientKey: 'foo', })这里需要注意大小写:插件选项中的属性名为queryClientKey(驼峰),组合后得到的是VUE_QUERY_CLIENT:foo。
在需要手动获取客户端时,也可以直接调用useQueryClient(id)并传入与queryClientKey相同的值。从 useQueryClient.ts 的实现可以看到,useQueryClient同样通过getClientKey(id)拼接键名后再inject:
export function useQueryClient(id = ''): QueryClient { // ensures that `inject()` can be used if (!hasInjectionContext()) { throw new Error( 'vue-query hooks can only be used inside setup() function or functions that support injection context.', ) } const key = getClientKey(id) const queryClient = inject<QueryClient>(key) if (!queryClient) { throw new Error( "No 'queryClient' found in Vue context, use 'VueQueryPlugin' to properly initialize the library.", ) } return queryClient }即:useQueryClient()默认注入VUE_QUERY_CLIENT,而useQueryClient('foo')注入VUE_QUERY_CLIENT:foo,与插件侧的自定义键严格对应。该行为在测试 useQueryClient.test.ts 中得到了验证:传入'foo'时inject被调用参数为`${VUE_QUERY_CLIENT}:${queryClientKey}`。
源码中的上下文注入细节
插件在不同 Vue 版本下的注入方式略有差异(见 vueQueryPlugin.ts):
- Vue 3 及兼容环境:调用
app.provide(clientKey, client); - Vue 2(通过 vue-demi 兼容):使用全局 mixin 的
beforeCreate钩子,将客户端写入组件实例的_provided对象,从而模拟 provide/inject 行为。
此外,useQueryClient会先检查hasInjectionContext(),若在setup()之外调用会直接抛出错误,这一边界行为同样有测试覆盖(useQueryClient.test.ts)。
内部实现验证
围绕自定义客户端,vue-query包中还有一组配套测试可以佐证上述行为:
- vueQueryPlugin.test.ts:覆盖
VueQueryPlugin.install传入queryClientKey(如'CUSTOM')时的安装行为; - useQueryClient.test.ts:覆盖默认键注入、自定义键后缀拼接、未安装插件时报错、
setup()外调用报错等场景。
同时,从 useBaseQuery.ts、useMutation.ts、useQueries.ts 等 hook 的实现可以看出,绝大多数组合式函数都遵循同一模式:优先使用选项里显式传入的queryClient,否则回退到useQueryClient()从上下文获取。这意味着自定义客户端一旦注入成功,所有查询、变更、无限查询等 hook 都会自动感知并使用它。
总结
| 场景 | 推荐配置 |
|---|---|
| 只需调整默认客户端参数 | queryClientConfig: { defaultOptions: {...} } |
| 需要提前创建、共享或注入外部客户端 | queryClient: myClient |
| 多应用/多客户端共存(尤其 Vue 2) | queryClientKey: 'Foo'并在查询选项中同步传递 |
| 手动取回客户端 | useQueryClient()或useQueryClient('foo') |
自定义客户端(Custom Client)是 Vue Query 灵活性的核心入口:queryClientConfig与queryClient分别对应“由插件托管”和“由应用托管”两种客户端生命周期,queryClientKey则通过${VUE_QUERY_CLIENT}:${key}的后缀拼接规则为多实例共存提供命名空间隔离。合理组合这三个选项,即可在任意复杂度的 Vue 应用中精确控制查询状态的注入、共享与隔离。
【免费下载链接】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),仅供参考