惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Microsoft Security Blog
Microsoft Security Blog
P
Proofpoint News Feed
C
CXSECURITY Database RSS Feed - CXSecurity.com
博客园 - 叶小钗
MongoDB | Blog
MongoDB | Blog
F
Full Disclosure
Martin Fowler
Martin Fowler
G
Google Developers Blog
F
Fortinet All Blogs
IT之家
IT之家
Blog — PlanetScale
Blog — PlanetScale
阮一峰的网络日志
阮一峰的网络日志
博客园 - 三生石上(FineUI控件)
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Google DeepMind News
Google DeepMind News
Google Online Security Blog
Google Online Security Blog
Hacker News: Ask HN
Hacker News: Ask HN
T
Tailwind CSS Blog
Cloudbric
Cloudbric
U
Unit 42
MyScale Blog
MyScale Blog
TaoSecurity Blog
TaoSecurity Blog
T
The Blog of Author Tim Ferriss
博客园 - 司徒正美
博客园 - Franky
AI
AI
爱范儿
爱范儿
L
LangChain Blog
小众软件
小众软件
D
DataBreaches.Net
M
MIT News - Artificial intelligence
GbyAI
GbyAI
Y
Y Combinator Blog
有赞技术团队
有赞技术团队
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
The Cloudflare Blog
Help Net Security
Help Net Security
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
P
Privacy International News Feed
H
Hackread – Cybersecurity News, Data Breaches, AI and More
D
Docker
A
About on SuperTechFans
Scott Helme
Scott Helme
The GitHub Blog
The GitHub Blog
V
V2EX
N
Netflix TechBlog - Medium
S
Security Affairs
Security Archives - TechRepublic
Security Archives - TechRepublic
H
Heimdal Security Blog
WordPress大学
WordPress大学

木灵鱼儿

从零开始:手把手教你封装一个企业级 Axios 请求模块 - 木灵鱼儿 03-Vue Query 高级进阶:应对复杂业务场景的硬核套路 - 木灵鱼儿 02-Vue Query 快速入门:从零构建你的第一个声明式查询 - 木灵鱼儿 git 如何将所有历史提交合并为一条 - 木灵鱼儿 Windows 下如何快速复制目录的同时排除指定的目录和文件 - 木灵鱼儿 x86多网口软路由+pve+爱快ikuai+iStoreOS实现组网和翻墙 - 木灵鱼儿 生产部署时动态导入 Chunk 失效的实用回退方案 - 木灵鱼儿 如何在 Vite 项目中优雅地展示用户协议?(Markdown 转 Vue 组件方案) - 木灵鱼儿 Vue 路由守卫进阶:用策略模式告别 if-else - 木灵鱼儿 16 Python for循环 - 木灵鱼儿 15 Python while循环 - 木灵鱼儿 如何生成一个“扫码连WIFI”的二维码 - 木灵鱼儿 14 Python 分支语句if - 木灵鱼儿 13 Python 字符串详解 - 木灵鱼儿 12 Python 语句、表达式与运算符 - 木灵鱼儿
01-异步状态管理新范式:为什么在 Vue 3 中使用 vue-query? - 木灵鱼儿
木灵鱼儿 · 2026-06-23 · via 木灵鱼儿

前言

在现代 Web 应用开发中,API 数据请求与状态同步是前端架构的核心环节。在 Vue 3 项目中,开发者通常习惯于使用 Axios 结合组件生命周期钩子(如 onMounted)来获取数据,并手动维护 loadingerror 等响应式状态。

然而,随着应用复杂度的提升,这种命令式的数据管理模式在性能、代码复用性以及用户体验上面临诸多挑战。本文将从架构设计的角度,探讨如何通过引入 @tanstack/vue-query 实现从“命令式数据获取”向“声明式异步状态管理”的范式转变,并阐述其与 Axios 的协同工作机制。


传统前端请求的痛点

在没有引入专门的异步状态管理工具前,一个典型的 Vue 3 数据请求组件通常采用如下模式:

<script setup lang="ts">
import { ref, onMounted } from "vue";
import { getUserDetail, type UserDetail } from "@/api/user";

const data = ref<UserDetail | null>(null);
const loading = ref(false);
const error = ref<Error | null>(null);

const fetchData = async () => {
 loading.value = true;
 error.value = null;
 try {
  data.value = await getUserDetail();
 } catch (err) {
  error.value = err as Error;
 } finally {
  loading.value = false;
 }
};

onMounted(() => {
 fetchData();
});
</script>

<template>
 <div v-if="loading">加载中...</div>
 <div v-else-if="error">出错了:{{ error.message }}</div>
 <div v-else-if="data">用户:{{ data.name }}</div>
</template>

这种命令式写法在简单场景下直观易懂,但在中大型项目中会暴露以下架构缺陷:

  1. 状态冗余与样板代码:每个需要请求数据的组件都必须重复定义 loadingerrordata 这一套响应式变量,并编写相似的 try-catch-finally 逻辑,导致代码冗余。
  2. 重复请求与带宽浪费:若页面中多个独立挂载的子组件依赖同一份数据(例如当前登录用户信息),它们各自挂载时会触发多次相同的 API 请求,缺乏统一的请求合并机制。
  3. 缓存缺失与页面闪烁:用户在不同路由间切换时,组件的销毁与重新挂载会频繁触发网络请求,导致页面出现“空白 -> 加载中 -> 渲染”的闪烁,无法提供即时响应的体验。
  4. 数据过期与同步困难:客户端难以感知服务器端数据的实时变化。当用户在 A 页面修改了数据,B 页面缓存的数据无法自动或优雅地同步更新。

核心概念:Axios 与 Vue Query 的本质区别

要解决上述痛点,首先需要理清网络传输与状态管理在前端架构中的职责边界。

  • Axios 的定位:传输层(Transport Layer)

    • 它是一个优秀的 HTTP 客户端,专注于“如何安全、正确地执行网络通信”
    • 其职责包括:请求/响应拦截、Token 刷新、超时处理、请求重试、数据序列化等网络细节。
    • 它不关心数据在客户端的生命周期、共享范围以及缓存策略。
  • Vue Query 的定位:异步状态管理层(Asynchronous State Management)

    • 它是一个强大的状态管理器,专注于“如何管理、同步和缓存客户端的服务器状态”
    • 其职责包括:数据缓存生命周期管理、请求去重、后台静默刷新、条件触发、垃圾回收等。
    • 它不关心底层的网络请求是通过 Axios、Fetch 还是 WebSocket 实现的。

协同工作模式

在优秀的架构设计中,两者并非竞争关系,而是互补的。Axios 负责发送请求,Vue Query 负责管理请求结果。

graph LR
    Component[Vue 组件] -->|useQuery| VueQuery[Vue Query 缓存层]
    VueQuery -->|未命中缓存/已过期| Axios[Axios 传输层]
    Axios -->|发起 HTTP 请求| Server[后端服务器]
    Server -->|返回数据| Axios
    Axios -->|交付数据| VueQuery
    VueQuery -->|响应式更新| Component

引入 Vue Query 带来的核心收益

声明式数据获取

开发者不再需要手动维护 loadingerror 状态。Vue Query 的 useQuery 会自动解构出这些状态,使组件逻辑更加聚焦于 UI 渲染:

const { data, isLoading, isError, error } = useQuery({
 queryKey: ["user-detail"],
 queryFn: getUserDetail
});

自动请求去重(Deduplication)

当多个组件在同一时刻调用相同的 useQuery(拥有相同的 queryKey)时,Vue Query 会将它们合并。底层的网络请求只会发送一次,所有组件共享同一个响应结果。

开箱即用的缓存与后台静默刷新(SWR 机制)

Vue Query 采用了经典的 SWR (Stale-While-Revalidate) 缓存失效策略:

  • 当组件挂载时,如果缓存中已有数据,Vue Query 会立即返回缓存数据,让用户瞬间看到内容。
  • 与此同时,它会在后台静默发起请求,获取最新数据。
  • 当最新数据返回后,它会静默更新缓存并响应式地渲染页面,确保数据最终一致性。

极简的全局状态共享

无需通过 Pinia 编写繁琐的 action 和 state,直接通过相同的 QueryKey 即可在任意组件间共享数据,降低了状态管理的复杂度。


项目实战:它们是如何在项目中优雅配合的?

在实际项目中,我们通过将 Axios 实例与 Vue Query 的全局配置进行解耦,实现了职责分离。

统一的初始化与职责分离

在项目初始化阶段,我们配置全局的 QueryClient。由于底层的 Axios 实例已经处理了网络层面的请求重试和异常拦截(我自己封装的axios),我们在 Vue Query 层进行了相应的适配:

import { QueryClient, VueQueryPlugin } from "@tanstack/vue-query";

export const queryClient = new QueryClient({
 defaultOptions: {
  queries: {
   // 1. 底层 Axios 已经实现了请求重试机制,所以这里关闭自动重试,避免双重重试
   retry: false,
   // 2. 默认关闭窗口聚焦时自动刷新,按需在单个接口开启,节省服务器流量
   refetchOnWindowFocus: false
  }
 }
});

声明式查询封装示例

在业务开发中,我们将 API 请求函数与 Vue Query 的缓存配置封装为统一的 Composable 函数。以下是一个标准的用户详情查询封装:

import { useQuery } from "@tanstack/vue-query";
import { getUserDetail } from "@/api/user"; // 封装好的 Axios 请求函数

export const USER_DETAIL_KEY = ["user-detail"];

export function useUserDetail(isLogin: Ref<boolean>) {
 return useQuery({
  queryKey: USER_DETAIL_KEY,
  queryFn: getUserDetail,
  // 只有在用户登录后,才允许发起请求(条件触发)
  enabled: isLogin,
  // 5分钟内,数据被认为是“新鲜”的,不需要重复请求
  staleTime: 5 * 60 * 1000
 });
}

通过这种设计,底层的 Axios 专注于处理网络传输细节,而上层的 useUserDetail 则专注于处理缓存有效期、登录依赖等业务状态逻辑,实现了清晰的关注点分离。


总结

从“命令式 Axios 请求”到“声明式 Vue Query 缓存管理”,改变的不仅是几行代码,更是前端架构思维的转变。

  • Axios 解决了“如何通信”的问题。
  • Vue Query 解决了“如何同步状态”的问题。

在下一篇文章中,我们将正式进入实战,手把手教你如何在项目中从零开始使用 Vue Query,并掌握其最核心的 API 与配置。