TanStack Vue Query 与 GraphQL 集成指南:基于 graphql-request 与代码生成的全类型化数据获取
【免费下载链接】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 是 TanStack Query 在 Vue 生态中的官方实现,其数据获取机制建立在 Promise 抽象之上,因此可以与包括 GraphQL 在内的任意异步数据获取客户端无缝协作。本文将以 docs/framework/vue/graphql.md(由 React 版对应文档 经replace: { 'React': 'Vue', 'react-query': 'vue-query' }规则自动生成)为核心骨架,系统讲解 Vue Query 与 GraphQL 的结合方式、类型安全边界,并给出基于graphql-request与 GraphQL Code Generator 的完整可运行方案。读完本文,你将掌握在 Vue 应用中用 Vue Query 驱动 GraphQL 查询、用代码生成工具获得端到端类型检查的完整能力。
为什么 Vue Query 能与 GraphQL 天然协作
Vue Query 的请求机制是"传输层无关"(transport/protocol/backend agnostic)的。官方文档明确说明:由于 Vue Query 的获取机制基于 Promise 构建,你可以将 Vue Query 与任何异步数据获取客户端配合使用,包括 GraphQL。这一点在 packages/vue-query/README.md 的快速特性列表中也得到印证:"Transport/protocol/backend agnostic data fetching (REST, GraphQL, promises, whatever!)"。
从源码结构看,这一能力源于其分层架构:useQuery只是薄薄的一层 Vue 响应式封装,底层委托给核心库的QueryObserver。以 packages/vue-query/src/useQuery.ts 为例:
export function useQuery<TQueryFnData, TError, TData, TQueryKey extends QueryKey>( options: MaybeRefOrGetter<UseQueryOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>>, queryClient?: QueryClient, ): UseQueryReturnType<TData, TError> | UseQueryDefinedReturnType<TData, TError> { return useBaseQuery(QueryObserver, options, queryClient) }queryFn的契约只有一个:返回一个 Promise。因此无论你内部调用fetch、Axios、graphql-request还是任何 GraphQL 客户端,只要最终产出 Promise,Vue Query 就能接管其状态管理、缓存、重试、去重与失效刷新。这意味着 GraphQL 集成不需要任何特殊适配器,直接"即插即用"。
一个必须知道的边界:不支持规范化缓存
在开始集成之前,官方文档特别提醒了一个重要边界:
请记住,Vue Query不支持规范化缓存(normalized caching)。虽然绝大多数用户实际上并不需要规范化缓存,甚至可能没有他们想象中那么受益,但确实存在极少数场景可能需要它——请务必先确认这真的是你所需要的功能。
这意味着:Vue Query 的缓存以"查询键 → 查询结果"的扁平结构存储,而不是像 Apollo Client 那样把每条实体记录规范化到全局 store 中再按引用拼接。对大多数应用而言,这反而更简单、可预测;但如果你确实依赖跨查询的实体级共享缓存,那么 Vue Query 并非为这种场景设计,需要评估其他方案。
准备工作:安装与初始化 Vue Query
要让 GraphQL 查询真正跑起来,首先需要一个已初始化 VueQueryPlugin 的 Vue 应用。根据 packages/vue-query/README.md,安装方式如下:
npm i @tanstack/vue-query # 或 pnpm add @tanstack/vue-query # 或 yarn add @tanstack/vue-query # 或 bun add @tanstack/vue-query注意:如果你使用的是 Vue 2.6,需要额外配置 @vue/composition-api 也给出了实际的依赖组合示例。
然后在入口文件中通过插件安装(参考 examples/vue/basic/src/main.ts):
import { createApp } from 'vue' import { VueQueryPlugin } from '@tanstack/vue-query' import App from './App.vue' createApp(App).use(VueQueryPlugin).mount('#app')初始化完成后,即可在任意组件的setup()中使用useQuery。
第一步:用 graphql-request 发起 GraphQL 查询
graphql-request是最轻量的 GraphQL 客户端之一,它把一个 GraphQL 文档和变量直接映射为一个 Promise,与 Vue Query 的queryFn契约完美契合。
仓库中提供了真实可参考的集成示例:examples/react/basic-graphql-request/(React 版演示,逻辑同样适用于 Vue)。其依赖组合(见 examples/react/basic-graphql-request/package.json)展示了核心依赖:graphql-request(^7.1.2)与graphql(^16.9.0)。在 Vue 项目中,只需把@tanstack/react-query换成@tanstack/vue-query即可。
一个基础的 Vue 组合式写法如下:
import { defineComponent } from 'vue' import { request, gql } from 'graphql-request' import { useQuery } from '@tanstack/vue-query' const endpoint = 'https://graphqlzero.almansi.me/api' type Post = { id: number title: string body: string } export default defineComponent({ name: 'Posts', setup() { const { status, data, error, isFetching } = useQuery({ queryKey: ['posts'], queryFn: async () => { const { posts: { data }, } = await request<{ posts: { data: Array<Post> } }>( endpoint, gql` query { posts { data { id title } } } `, ) return data }, }) return { status, data, error, isFetching } }, })这里的要点:
queryKey是缓存的唯一标识:['posts']标识该查询的缓存条目,Vue Query 据此完成去重、失效与后台刷新。queryFn返回 Promise:graphql-request的request()天然满足这一要求,无需额外包装。- 返回值为响应式:Vue Query 通过 Vue 的
reactive/ref机制暴露status、data、error、isFetching等状态(见 packages/vue-query/src/useBaseQuery.ts 的实现),模板中可直接使用。 - 首次访问加载、二次访问秒开:示例中明确指出,访问过的查询再次进入时会"instant load + background refresh",这正是 Vue Query 缓存与后台刷新的效果。
第二步:类型安全与代码生成(GraphQL Code Generator)
手写gql模板字符串虽然可用,但无法获得字段级类型检查。官方文档推荐的正规方案是:Vue Query +graphql-request@5++ GraphQL Code Generator,三者组合可提供完全类型化的 GraphQL 操作(fully-typed GraphQL operations)。
工作流如下:
- 在项目中安装并配置 GraphQL Code Generator,让它根据你的 GraphQL schema 和操作文档生成类型定义;
- 从生成的
./gql/gql模块导入graphql函数; - 用这个
graphql()函数包裹查询文档,Code Generator 会为每个操作生成对应的类型; - 将这些类型化文档传给
graphql-request与useQuery,即可获得端到端类型检查——data完全类型化,连查询变量(variables)也经过类型检查。
完整示例:类型化查询 + 变量检查
以下示例来自官方文档(已转换为 Vue 语法),演示了带变量查询的完整类型化链路:
import { defineComponent } from 'vue' import request from 'graphql-request' import { useQuery } from '@tanstack/vue-query' import { graphql } from './gql/gql' const allFilmsWithVariablesQueryDocument = graphql(/* GraphQL */ ` query allFilmsWithVariablesQuery($first: Int!) { allFilms(first: $first) { edges { node { id title } } } } `) export default defineComponent({ name: 'Films', setup() { // data 是完全类型化的! const { data } = useQuery({ queryKey: ['films'], queryFn: async () => request( 'https://swapi-graphql.netlify.app/.netlify/functions/index', allFilmsWithVariablesQueryDocument, // 变量同样经过类型检查! { first: 10 }, ), }) return { data } }, })这段代码中:
graphql()包裹的模板字符串会被 Code Generator 解析,生成AllFilmsWithVariablesQueryDocument这一携带类型信息的文档对象;request()的第二个参数接收该文档后,第三个参数{ first: 10 }会被约束为{ first: number }——如果传错字段名或类型,TypeScript 直接报错;useQuery的data会基于文档的返回类型被推断为{ allFilms: { edges: { node: { id: string; title: string } }[] } }的结构,模板渲染时享受完整补全与校验。
如果项目使用 Vue 的<script setup>语法,可进一步简化为:
<script setup lang="ts"> import request from 'graphql-request' import { useQuery } from '@tanstack/vue-query' import { graphql } from './gql/gql' const allFilmsWithVariablesQueryDocument = graphql(/* GraphQL */ ` query allFilmsWithVariablesQuery($first: Int!) { allFilms(first: $first) { edges { node { id title } } } } `) const { data } = useQuery({ queryKey: ['films'], queryFn: async () => request( 'https://swapi-graphql.netlify.app/.netlify/functions/index', allFilmsWithVariablesQueryDocument, { first: 10 }, ), }) </script> <template> <ul> <li v-for="edge in data?.allFilms.edges" :key="edge.node.id"> {{ edge.node.title }} </li> </ul> </template>Mutation 的代码生成配合
类型化并不局限于查询。Vue Query 的useMutation同样接受返回 Promise 的mutationFn(源码见 packages/vue-query/src/useMutation.ts),因此代码生成的 mutation 文档可以直接传入graphql-request:
import { defineComponent } from 'vue' import request from 'graphql-request' import { useMutation, useQueryClient } from '@tanstack/vue-query' import { graphql } from './gql/gql' const createReviewMutationDocument = graphql(/* GraphQL */ ` mutation createReview($input: ReviewInput!) { createReview(input: $input) { id stars commentary } } `) export default defineComponent({ name: 'CreateReview', setup() { const queryClient = useQueryClient() const { mutate, isPending, isError, error } = useMutation({ mutationFn: (variables: { input: { stars: number; commentary: string } }) => request( 'https://swapi-graphql.netlify.app/.netlify/functions/index', createReviewMutationDocument, variables, ), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['films'] }) }, }) return { mutate, isPending, isError, error } }, })这里mutationFn的入参variables可以显式绑定到文档推断出的变量类型,成功后可调用invalidateQueries让依赖的查询自动重新获取——这正是 Vue Query 管理"写操作后的数据同步"的惯用姿势。
进阶:用响应式选项驱动动态 GraphQL 查询
Vue 版的useQuery相比 React 版有一个重要差异:options 支持传入响应式值(MaybeRefOrGetter),当queryKey、enabled等发生变更时会自动重新执行查询。这在 GraphQL 场景下尤其适合"根据当前状态改变查询变量"的需求。
官方 README(packages/vue-query/README.md)与 useQuery 类型定义 共同说明了这一点:
import { ref } from 'vue' import { request, gql } from 'graphql-request' import { useQuery } from '@tanstack/vue-query' const id = ref(1) const enabled = ref(false) const { data } = useQuery({ queryKey: ['post', id], // queryKey 可以是 ref queryFn: () => request( 'https://graphqlzero.almansi.me/api', gql` query post($id: ID!) { post(id: $id) { id title body } } `, { id: id.value }, ), enabled, // enabled 也可以是 ref })当id.value变化时,queryKey随之变化,Vue Query 会以新键发起新查询并保留旧数据的缓存;当enabled为false时查询保持挂起状态。这一机制让"分页、详情联动、条件加载"等 GraphQL 场景的实现非常直观。
结合仓库继续深入
- 官方文档:本文对应文档为 docs/framework/vue/graphql.md,其内容由 docs/framework/react/graphql.md 经替换规则生成;Vue Query 的完整入门见 docs/framework/vue/overview.md,快速开始见 docs/framework/vue/quick-start.md。
- 源码实现:查询入口 packages/vue-query/src/useQuery.ts、变更入口 packages/vue-query/src/useMutation.ts,以及底层
useBaseQuery实现 packages/vue-query/src/useBaseQuery.ts。 - 可运行示例:graphql-request 的完整请求/缓存演示见 examples/react/basic-graphql-request/;Vue 项目的插件初始化与查询用法见 examples/vue/basic/ 与 examples/vue/simple/。
- 包说明:安装、初始化与响应式选项的官方说明见 packages/vue-query/README.md。
小结
Vue Query 与 GraphQL 的结合不需要任何适配层:只要queryFn返回 Promise,Vue Query 就能完整接管状态管理与缓存;配合graphql-request和 GraphQL Code Generator,还能获得"文档即类型"的端到端类型安全体验。在动手集成前,请记住文档中反复强调的边界——Vue Query 提供的是扁平化查询缓存而非规范化缓存,明确这一点后,你就能在绝大多数应用场景中安全地享受这套轻量、可预测的 GraphQL 数据获取方案。
【免费下载链接】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),仅供参考