TanStack Query :现代 Web 应用的异步状态管理利器

一、 核心定位:强大的异步状态管理库

TanStack Query 是一个为 JavaScript/TypeScript 应用设计的、功能极其强大的异步状态管理库。它的主要职责是帮助开发者获取 (fetching)、缓存 (caching)、同步 (synchronizing) 和更新 (updating) 来自服务器或任何异步来源的数据状态

虽然它最初因 React 生态而闻名(即 React Query),但现在它已经发展成为一个框架无关 (Framework Agnostic) 的库,官方提供了针对 React, Vue, Svelte, Solid 甚至原生 JavaScript 的适配器 (@tanstack/react-query, @tanstack/vue-query, 等)。

二、 解决的核心痛点:为什么需要它?

在现代 Web 应用中,直接使用 fetchaxios 等工具从 API 获取数据很简单。然而,真正的挑战在于如何管理这些异步获取的数据的状态,这通常涉及到许多复杂且重复的问题:

  1. 缓存 (Caching): 如何避免对相同的数据进行不必要的重复请求?如何智能地管理缓存的有效期?
  2. 数据同步与更新 (Data Synchronization & Updates): 服务器上的数据可能随时改变,如何让客户端的数据保持“新鲜”?如何在用户不操作的情况下,在后台自动更新可能过时的数据?
  3. 过时数据处理 (Stale Data): 如何界定数据何时可能“过时”(stale),并在需要时触发更新,同时又能快速显示旧数据以优化体验?
  4. 加载与错误状态管理 (Loading & Error States): 如何优雅地处理数据正在加载中、加载成功、加载失败等多种状态,并将其反映在 UI 上?
  5. 分页与无限滚动 (Pagination & Infinite Scrolling): 如何简化这些常见的 UI 模式,管理好多页数据的获取和状态?
  6. 数据变更操作 (Mutations): 如何处理创建 (POST)、更新 (PUT/PATCH)、删除 (DELETE) 等改变服务器数据的操作?这些操作完成后,如何有效地更新相关的已缓存数据(比如让列表数据重新获取)?
  7. 乐观更新 (Optimistic Updates): 如何在数据变更请求发送到服务器(并等待响应)之前,就“乐观地”更新 UI,给用户即时反馈,并在操作失败时自动回滚 UI?
  8. 请求去重 (Request Deduplication): 如何避免在短时间内对同一资源发起多个相同的请求?
  9. 性能优化 (Performance): 如何通过智能缓存、后台更新、减少不必要的渲染等方式提升应用性能和用户体验?

传统的客户端状态管理库(如 Redux, Zustand, Vuex, Pinia)主要设计用来管理客户端 UI 状态(如模态框开关、表单输入、主题等)。用它们来管理服务器状态通常需要编写大量手动的异步逻辑、缓存策略、状态同步代码,非常繁琐且容易出错。

三、 TanStack Query 的核心理念:服务器状态视为“外部”状态

TanStack Query 认为来自服务器的数据是一种外部状态,客户端并不完全“拥有”或控制它。客户端的主要任务是与这个服务器状态进行同步,并在本地进行智能缓存和管理。

四、 主要概念和功能

  1. 查询 (Queries): useQuery (React) / createQuery (Core)

    • 用途: 主要用于从服务器获取 (读取) 数据。
    • 核心要素:
      • queryKey (查询键): 必需。一个用于唯一标识该查询数据的数组或字符串。这是 TanStack Query 识别和缓存数据的关键。键的设计很重要,通常包含资源名称和参数,如 ['todos'], ['todos', { status: 'done', page: 1 }], ['post', postId]
      • queryFn (查询函数): 必需。一个返回 Promise 的异步函数,负责执行实际的数据获取逻辑(例如,调用 axios.get('/api/todos'))。
      • options (配置项): 大量的可选配置项,用于精细控制查询行为,常见的有:
        • staleTime: 数据在多长时间内被认为是“新鲜”的(在此时间内不会触发后台重新获取),默认 0
        • cacheTime (v5 前叫 cacheTime, v5 后叫 gcTime - Garbage Collection Time): 查询实例在没有活跃观察者后,数据在缓存中保留多长时间才会被垃圾回收,默认 5 分钟。
        • refetchOnWindowFocus: 窗口重新获得焦点时是否自动重新获取,默认 true
        • refetchOnMount: 组件挂载时是否重新获取(如果数据已过时),默认 true
        • refetchInterval: 设置轮询间隔,自动重新获取数据。
        • enabled: 控制查询是否自动执行。
        • retry: 失败时自动重试的次数或配置。
    • 返回值: 返回一个包含查询状态和数据的对象,常用的有:
      • data: 成功获取的数据 (类型为 TDataundefined)。
      • status: 查询的详细状态 ('pending', 'error', 'success')。
      • isPending: 是否正在首次加载中 (v5 推荐使用)。
      • isLoading: (v5 之前的版本常用) 是否首次加载中。
      • isFetching: 是否正在进行任何形式的数据获取(包括首次加载和后台刷新)。
      • isSuccess: 是否已成功获取数据。
      • isError: 是否发生了错误。
      • error: 具体的错误对象 (类型为 TErrornull)。
      • refetch: 一个手动触发重新获取该查询的函数。
      • isStale: 数据是否已过时。
  2. 变更 (Mutations): useMutation (React) / createMutation (Core)

    • 用途: 主要用于执行改变服务器数据的操作,如创建 (POST)、更新 (PUT/PATCH)、删除 (DELETE)。这些操作通常有副作用。
    • 核心要素:
      • mutationFn (变更函数): 必需。一个返回 Promise 的异步函数,负责执行实际的数据变更操作(例如,调用 axios.post('/api/todos', newTodo))。接收一个变量作为参数。
      • options (配置项): 用于控制变更的行为和副作用,常见的有:
        • onSuccess(data, variables, context): 变更成功后的回调。极其常用,通常在这里使相关的查询失效 (queryClient.invalidateQueries) 来触发数据更新,或者手动更新查询缓存 (queryClient.setQueryData)
        • onError(error, variables, context): 变更失败后的回调。
        • onSettled(data, error, variables, context): 无论成功或失败都会执行的回调。
        • onMutate(variables): 在执行 mutationFn 之前同步执行的回调。常用于实现乐观更新,它应该返回一个上下文对象,该对象可以在 onErroronSettled 中用于回滚。
    • 返回值: 返回一个包含变更状态和触发函数的对象,常用的有:
      • mutate(variables, options?): 触发变更操作的函数(不返回 Promise)。
      • mutateAsync(variables, options?): 触发变更操作并返回 Promise 的函数。
      • status: 变更状态 ('idle', 'pending', 'error', 'success')。
      • isPending: 是否正在执行变更。
      • isSuccess, isError, error, data 等。
  3. 查询客户端 (QueryClient)

    • 核心: TanStack Query 的中心管理器和缓存存储。通常在应用根部创建一个实例,并通过 Provider (React) 或 Plugin (Vue) 提供给整个应用。
    • 功能:
      • 存储和管理所有查询的缓存数据。
      • 提供全局默认配置。
      • 提供手动与缓存交互的 API,非常重要:
        • invalidateQueries({ queryKey, ... }): 使匹配的查询失效。这是最常用的更新策略,失效的查询在下次被观察到或满足其他 refetch 条件时会自动重新获取。
        • refetchQueries({ queryKey, ... }): 强制重新获取匹配的查询。
        • setQueryData(queryKey, data | updaterFn): 直接手动更新某个查询的缓存数据。
        • getQueryData(queryKey): 手动读取某个查询的缓存数据。
        • removeQueries({ queryKey, ... }): 从缓存中移除查询。
        • ...还有更多高级 API。
  4. 缓存机制详解

    • Stale-While-Revalidate (默认策略): 这是 TanStack Query 缓存行为的核心。
      • Stale Time (staleTime): 定义了数据在被视为“过时 (stale)”之前保持“新鲜 (fresh)”状态的时间。在此时间内,即使组件重新挂载或窗口聚焦,也不会触发后台重新获取。默认 0,意味着数据在获取后立即变为过时。
      • Cache Time / GC Time (cacheTime / gcTime): 定义了当一个查询不再有任何活跃的 useQuery 实例(即没有组件正在使用它)之后,它的数据会在缓存中保留多长时间才会被垃圾回收 (Garbage Collection) 并从内存中删除。默认 5 分钟。
      • 工作流程:
        1. 当你首次使用 useQuery 获取数据时,会发起请求,状态为 pending,成功后变为 success,数据存入缓存。
        2. 如果 staleTime 未过,再次访问该查询时,直接返回缓存中的新鲜数据,isFetchingfalse
        3. 如果 staleTime 已过(默认情况是立即过时),再次访问该查询时:
          • 立即返回缓存中的旧(stale)数据,让 UI 快速显示内容。
          • 同时在后台发起一次静默的重新获取请求 (isFetching 变为 true)
          • 如果后台请求成功,数据会被更新到缓存中,并通知相关组件更新 UI。
          • 如果后台请求失败,则保留旧数据,并记录错误。
        4. 如果一个查询长时间没有被使用(超过 gcTime),它的缓存数据会被彻底删除。

五、 核心优势与收益

  • 极大减少模板代码: 无需手动编写缓存逻辑、加载/错误状态管理、数据同步等代码。
  • 提升用户体验: 通过缓存、后台更新、stale-while-revalidate 和乐观更新等机制,让应用感觉更快、响应更灵敏。
  • 改善性能: 自动去重请求,智能缓存减少不必要的网络调用。
  • 更好的开发者体验: API 设计直观,心智负担小,能更专注于业务逻辑。使代码更具声明性。
  • 状态可预测性: 服务器状态的管理变得更加结构化和可预测。
  • 强大的 DevTools: 提供出色的浏览器开发者工具扩展,可以检查缓存状态、触发事件、观察查询细节等,极大方便调试。

六、 常见应用场景

  • 几乎所有需要从 API 获取并展示数据的场景。
  • 需要实现数据缓存以提升性能的应用。
  • 需要处理复杂加载、错误、刷新状态的 UI。
  • 实现分页、无限滚动列表。
  • 执行 CRUD 操作并需要更新相关视图的应用。
  • 希望实现乐观更新以优化交互的场景。

七、 如何开始

访问 TanStack Query 的官方文档 (tanstack.com/query/lates…),根据你使用的框架(React, Vue, etc.)查看对应的安装和使用指南。

总结:

TanStack Query是一个现代 Web 开发中强烈推荐用于管理服务器状态的库。它通过一套精心设计的抽象和强大的功能,极大地简化了异步数据处理的复杂性,显著提高了开发效率、应用性能和用户体验。如果你还在手动管理服务器状态,那么学习和使用 TanStack Query 很可能会给你的开发工作带来质的飞跃。

原文:https://juejin.cn/post/7491745468236988426

### Vue `useQuery` Hook 的使用方法及示例 在 Vue 生态系统中,虽然官方并没有直接提供名为 `useQuery` 的钩子函数,但是社区内存在许多类似的解决方案来处理数据获取和缓存的需求。通常情况下,开发者会借助第三方库如 `@tanstack/vue-query` 或者基于 Composition API 自定义实现。 #### 安装依赖包 为了能够在项目里边利用到类似于 React Query 功能的插件,在开始前需要安装相应的 npm 包: ```bash npm install @tanstack/vue-query axios ``` #### 基本配置 接下来初始化并设置好全局选项以便于后续调用: ```javascript // main.js or wherever you setup your app instance. import { createApp } from 'vue'; import App from './App.vue'; import { QueryClient, QueryClientProvider } from '@tanstack/vue-query'; const queryClient = new QueryClient(); createApp(App).use(QueryClientProvider,{ client:queryClient, }).mount('#app'); ``` #### 创建查询函数 下面展示如何创建一个简单的 GET 请求以获取远程资源列表,并将其封装成可重用的组合式函数形式供其他组件调用: ```typescript // src/composables/usePosts.ts import { ref } from "vue"; import { useQuery } from "@tanstack/vue-query"; async function fetchPosts() { const response = await fetch('https://jsonplaceholder.typicode.com/posts'); if (!response.ok) throw Error(response.statusText); return await response.json(); } export default function usePosts() { const posts = ref([]); const result = useQuery(['posts'], () => fetchPosts(),{ onSuccess(data){ posts.value=data; } }); return { ...result, posts }; } ``` 此代码片段展示了如何通过 `fetchPosts()` 函数发起 HTTP 请求,并将返回的数据存储在一个响应式的变量 `posts` 中。当成功接收到服务器端传回的结果时,则更新这个变量的内容[^4]。 #### 组件中的应用实例 现在可以很容易地把上面定义好的 hook 应用于任意 Vue SFC (Single File Component),如下所示: ```html <template> <div v-if="isFetching">Loading...</div> <ul v-else> <li v-for="(post,index) of posts" :key="index">{{ post.title }}</li> </ul> </template> <script lang="ts"> import { defineComponent } from 'vue' import usePosts from '@/composables/usePosts' export default defineComponent({ name: 'PostList', setup(){ const { isFetching,error,posts}=usePosts(); return{isFetching,error,posts}; }, }) </script> ``` 这段模板部分简单明了地呈现了一个待加载提示符以及最终渲染出来的帖子标题列表;而在脚本标签内部则引入了先前编写的自定义 hooks 并解构出所需属性传递给视图层进行显示。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值