在 Vue 项目中,大多数团队都会对 Axios 进行二次封装,统一处理请求拦截、错误提示、Token 注入等。这套方案我们称为“传统请求封装”。而 TanStack Query(Vue Query)在前端请求管理上引入了一套不同的模式——以服务端状态为中心,把请求结果当作缓存来管理。下面从真实开发场景出发,对比两者的优劣。
传统请求封装:你在手动管理一切
典型的传统封装长这样:
// api/user.js
import request from '@/utils/request'
export const getUserInfo = () => request.get('/api/user/info')
// 组件中使用
import { getUserInfo } from '@/api/user'
const userInfo = ref(null)
const loading = ref(false)
onMounted(async () => {
loading.value = true
try {
userInfo.value = await getUserInfo()
} catch (e) {
// 处理错误
} finally {
loading.value = false
}
})
它的优势是简单直接:你发起一个请求,拿到数据,存到组件本地状态,界面就渲染了。所有流程都在你的掌控之中,没有额外的抽象层。
但痛点也很明显:
- Loading / Error 状态需要手动维护。每个请求都要定义
loading、error变量,写try/catch/finally,代码重复量极大。 - 数据缓存完全靠自己。如果多个页面都需要用户信息,要么每次都请求一遍(浪费流量),要么把数据存到全局 Store 里手动同步(数据何时过期?谁来清除?)。
- 请求竞态问题。快速切换筛选条件时,后发的请求可能比先发的请求更早返回,导致界面显示旧数据。你需要手动添加请求标识或取消令牌。
- 乐观更新、分页、无限滚动等高级特性需要自己实现,代码量陡增。
TanStack Query:把请求当缓存管理
同样的场景,用 Vue Query 会变成:
import { useQuery } from '@tanstack/vue-query'
import { getUserInfo } from '@/api/user'
const { data: userInfo, isLoading, error } = useQuery({
queryKey: ['userInfo'],
queryFn: getUserInfo,
staleTime: 5 * 60 * 1000 // 5分钟内不重新请求
})
优势一目了然:
- 自动管理请求状态。
isLoading、isError、data等状态由库返回,你不再需要定义任何辅助变量。 - 内置缓存与去重。相同的
queryKey在staleTime内不会重复请求,而是直接从缓存读取;同时多个组件使用同一个 query 时,也会合并请求,避免重复网络调用。 - 竞态问题自动解决。Vue Query 会丢弃过期的请求响应,只使用最新的。
- 开箱即用的高级功能。分页(
useQuery加参数)、无限滚动(useInfiniteQuery)、乐观更新(useMutation的onMutate)、后台自动刷新(refetchInterval)等都内置或极简实现。 - 请求与组件解耦。数据获取逻辑可独立于组件,在父组件或路由守卫中预取,提升用户体验。
劣势与适用边界
TanStack Query 不是银弹,它也有不适用的场景:
- 简单场景下反而增加复杂度。如果只是几个页面里偶尔用到的接口,用传统封装配合
fetch或 Axios 更直接,引入 Query 的queryKey管理、QueryClientProvider等概念反而多余。 - 学习成本。团队需要理解
staleTime、cacheTime、invalidateQueries等缓存策略,以及 mutation 与 query 的区别,这对新手不友好。 - 非 RESTful 接口适配成本。如果后端接口设计不符合“请求-响应”范式(比如 WebSocket 推送、GraphQL),Query 的缓存模型需要额外定制,不一定比原生写法省心。
- 请求封装层依然需要。Query 本身不负责请求发送,
queryFn内部还是要用你自己封装的 Axios 或 fetch 实例,所以传统封装中的 Token 注入、错误统一处理依然存在,只是换了个地方写。
选型建议
| 场景 | 推荐方案 |
|------|----------|
| 管理后台、数据密集型应用 | TanStack Query,减少数据同步样板代码 |
| 请求少、交互简单的页面 | 传统 Axios 封装,保持轻量 |
| 实时性要求高的场景(如看板) | 传统封装 + WebSocket,或结合 Query 的 refetchInterval |
| 逐渐迁移 | 可以两者共存,核心数据用 Query,一次性操作仍用 Axios 直接调用 |
一句话总结:TanStack Query 解决的是“怎么把服务端数据舒服地同步到 UI”的问题,而不是“怎么发请求”的问题。如果你的项目里 loading、error、缓存失效这些代码占比过高,它就是值得投入的方案;如果你只是偶尔调个接口渲染一下页面,传统封装已经足够。