airi 仓库前端实战:在 Vue 3 项目中用 VueUse useAxios 以响应式方式驱动 axios 请求
导读
useAxios 是 VueUse 生态中位于 @vueuse/integrations 的集成型组合式函数(composable),本质上是给 axios 穿上一层 Vue 响应式外壳:它把请求结果、错误、加载态全部收拢进 ref,并额外提供 execute、abort、cancel 等生命周期控制能力。本文基于 airi 仓库内置的 VueUse 技能文档(.agents/skills/vueuse-functions/references/useAxios.md)展开,先讲清安装、五种调用形态与全部 Options 参数,再结合仓库中 VueUse 技能规则(SKILL.md)与依赖配置,说明这套模式在 Vue 3 / Nuxt 3 项目里的实际落地要点。读完本文,你将能够在任何 Vue 3 项目中用最少的样板代码写出可中断、可重试、可 await 的响应式 HTTP 请求。
一、定位:集成层组合式函数,需要显式引入
在 airi 仓库内置的 VueUse 技能分类中,useAxios 被归类在 @Integrations(第三方库集成)分类下,与 useAsyncValidator、useCookies、useFocusTrap、useSortable 等并列,其定位就是 axios 的响应式包装器(Wrapper)。
与 useLocalStorage、useDark 这类开箱即用的核心函数不同,技能文档明确将 useAxios 的调用规则(Invocation)标记为 EXTERNAL,其含义是(见 SKILL.md):
仅当用户已经安装了所需的外部依赖时才使用;否则需要重新考虑,只有真正必要时才要求安装。
也就是说,useAxios 强依赖 axios 运行时库,项目中必须先有 axios 才能使用它。这一规则对 airi 仓库同样适用:仓库的依赖目录清单 pnpm-workspace.yaml 中声明了 @vueuse/core(^14.4.0)与 @vueuse/shared(^14.4.0),但 @vueuse/integrations 与 axios 并不在公共依赖目录中;更值得注意的是,pnpm-lock.yaml 显示仓库通过 pnpm 的协议重映射把 axios 解析为 feaxios@^0.0.23 这一 fork 实现。因此在 airi 仓库中引入 useAxios 之前,需要先确认目标子包是否已声明依赖、以及 fork 后的 axios 实现是否满足需求——这正是 EXTERNAL 规则存在的意义。
二、安装:一行命令,锁定 axios 1.x
useAxios 本体随 @vueuse/integrations 一起发布,而它依赖的 axios 需要单独安装:
npm i axios@^1
按 VueUse 技能文档的说明,axios 需要 1.x 大版本(axios@^1)。若使用 pnpm workspace(airi 仓库即采用 pnpm workspace 管理),可以在对应子包的 package.json 中声明:
{
"dependencies": {
"axios": "^1",
"@vueuse/integrations": "^14.4.0"
}
}
安装完成后即可从集成入口导入:
import { useAxios } from '@vueuse/integrations/useAxios'
三、基础用法:一行代码拿到响应式请求状态
最简调用只需传入 URL,请求会自动立即发起(immediate 默认为 true):
import { useAxios } from '@vueuse/integrations/useAxios'
const { data, isFinished } = useAxios('/api/posts')
与手写 axios.get().then() 不同,这里的 data、isFinished 都是 Vue Ref,模板中可以自动解包、watch 中可以随时订阅,无需手动管理状态同步。
3.1 返回值总览
技能文档给出了完整的返回值清单,整理如下:
| 属性 | 类型 | 说明 |
|---|---|---|
data |
Ref<T> |
响应数据(响应体) |
response |
Ref<AxiosResponse> |
完整的 axios 响应对象 |
error |
Ref<unknown> |
请求失败时的错误信息 |
isFinished |
Ref<boolean> |
请求是否已完成(成功或失败均视为完成) |
isLoading |
Ref<boolean> |
请求是否进行中 |
isAborted |
Ref<boolean> |
请求是否被中止 |
abort / cancel |
() => void |
中止当前请求(两者互为别名) |
execute |
(url?, config?) => Promise |
执行 / 重新执行请求 |
其中 data 的类型细节取决于是否传入了 initialData:根据类型声明(详见第六节),若传入 initialData 则 data 为 Ref<T>,否则为 Ref<T | undefined>;response 与 error 在内部实现为 ShallowRef(ShallowRef<R | undefined>、ShallowRef<unknown | undefined>),并额外暴露了 isCanceled 作为 isAborted 的别名(见 useAxios.md)。
四、传入 Axios 实例:复用拦截器与基础配置
真实项目中通常会用 axios.create() 预先配置 baseURL、超时、请求/响应拦截器等。useAxios 支持把自定义实例作为第二参数传入:
import { useAxios } from '@vueuse/integrations/useAxios'
import axios from 'axios'
const instance = axios.create({
baseURL: '/api',
})
const { data, isFinished } = useAxios('/posts', instance)
此时 URL 会与实例的 baseURL 合并(最终请求 /api/posts),实例上注册的拦截器、默认 headers、认证逻辑也会一并生效。这是团队项目中推荐的做法——把鉴权、日志、错误统一处理都收敛在实例层。
五、请求配置覆盖:第三种传参形态
如果既要使用实例、又要在本次请求上覆盖配置(例如改走 POST),可以同时传入配置与实例:
import { useAxios } from '@vueuse/integrations/useAxios'
import axios from 'axios'
const instance = axios.create({
baseURL: '/api',
})
const { data, isFinished } = useAxios('/posts', { method: 'POST' }, instance)
这里的 { method: 'POST' } 是标准的 AxiosRequestConfig,任何 axios 支持的配置项(params、headers、timeout、auth、data 等)都可以在此覆盖。技能文档中展示的 useAxios 重载签名(见 useAxios.md)也印证了参数形态的灵活性:
useAxios(url)或useAxios(url, config?)或useAxios(url, instance?)或useAxios(url, config, instance)- 不传 URL 时:
useAxios(config?)、useAxios(instance?)、useAxios(config, instance),此时返回EasyUseAxiosReturn
六、手动执行:immediate: false 与 execute 的三种用法
默认行为是「传入 URL 立即请求」。当你不希望请求自动发起时,不传 URL 即可,此时 execute 成为唯一触发入口:
import { useAxios } from '@vueuse/integrations/useAxios'
const { execute } = useAxios()
execute(url)
execute 的灵活性体现在它可以接受不同形态的参数:
1. URL 替换:先给 useAxios 传 URL,execute 再传入新 URL 时,新 URL 会替换旧 URL:
import { useAxios } from '@vueuse/integrations/useAxios'
const { execute } = useAxios(url1, {}, { immediate: false })
execute(url2) // 请求 url2,而不是 url1
2. 仅传配置:execute 也接受纯配置对象,适合对同一地址发起多次参数不同的请求:
import { useAxios } from '@vueuse/integrations/useAxios'
const { execute } = useAxios(url1, { method: 'GET' }, { immediate: false })
execute({ params: { key: 1 } })
execute({ params: { key: 2 } })
这一形态非常适合「搜索 / 筛选 / 翻页」类场景:地址不变、参数变化,只需反复调用 execute({ params }),无需重建组合式函数。从类型签名看,execute 的参数被定义为 url?: string | AxiosRequestConfig<D>(见 useAxios.md),即 URL 与配置二选一或可组合,返回值为 Promise 化的组合式函数返回值。
七、await 结果:返回对象是 thenable
useAxios 的返回值同时是一个 thenable 对象,因此可以像 Promise 一样被 await——这是「组合式 + 命令式」混合编程的关键能力:
import { useAxios } from '@vueuse/integrations/useAxios'
const { data, isFinished, error } = await useAxios('/api/posts')
// data 此时已被填充
也可以只 await execute 的返回值,等待某次手动触发的请求结束:
import { useAxios } from '@vueuse/integrations/useAxios'
const { execute } = useAxios()
const result = await execute(url)
两种方式各有适用场景:前者适合页面初始化时同步等待首屏数据;后者适合「按钮点击 → 等待结果 → 处理副作用」的事件驱动流程。类型声明中 StrictUseAxiosReturn<T, R, D, O> & Promise<StrictUseAxiosReturn<T, R, D, O>>(见 useAxios.md)正是对这一特性的精确描述——返回值既可直接解构响应式状态,也可整体 await。
八、Options 全参数解析:从默认值到回调钩子
useAxios 的第四个参数是组合式函数自身的选项(注意与第三个参数 axios 配置区分开)。技能文档给出了完整示例:
const { data } = useAxios('/api/posts', config, instance, {
// 是否立即执行请求(提供 url 时默认 true)
immediate: true,
// 是否用 shallowRef 存放 data(默认 true)
shallow: true,
// 新请求发起时是否中止上一个未完成的请求(默认 true)
abortPrevious: true,
// 执行前是否将 data 重置为初始值(默认 false)
resetOnExecute: false,
// 初始数据值
initialData: [],
// 回调钩子
onSuccess: data => console.log('Success:', data),
onError: error => console.error('Error:', error),
onFinish: () => console.log('Finished'),
})
对照类型声明(useAxios.md),逐项说明:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
immediate |
boolean |
提供 url 时 true |
组合式函数创建时是否自动发起请求 |
shallow |
boolean |
true |
使用 shallowRef 存放数据,避免深层响应化带来的开销;数据体很大时保持默认即可 |
abortPrevious |
boolean |
true |
每次新 execute 时自动中止上一个未完成的请求,天然防止竞态 |
resetOnExecute |
boolean |
false |
每次执行前把 data 重置为初始状态,适合「刷新列表时先清空旧数据」的交互 |
initialData |
T |
无 | 初始数据;传入后 data 类型收紧为 Ref<T>(而非 `Ref<T |
onSuccess |
(data: T) => void |
无 | 请求成功回调,直接收到解包后的响应数据 |
onError |
(e: unknown) => void |
无 | 请求失败回调 |
onFinish |
() => void |
无 | 请求结束(无论成败)回调,适合统一关闭 loading |
选项类型上,UseAxiosOptions<T> 被定义为联合类型(见 useAxios.md):
export type UseAxiosOptions<T = any> =
| UseAxiosOptionsBase<T>
| UseAxiosOptionsWithInitialData<T>
即「带初始数据」与「不带初始数据」两条路径,TypeScript 会根据是否传入 initialData 自动收紧 data 的类型。
九、返回类型体系:Strict 与 Easy 两套形态
技能文档末尾给出了完整的类型声明,其中最有价值的是返回值被划分为两套接口:
StrictUseAxiosReturn<T, R, D, O>(严格形态):execute的签名是(url?: string | AxiosRequestConfig<D>, config?: AxiosRequestConfig<D>) => Promise<StrictUseAxiosReturn<T, R, D, O>>(useAxios.md),URL 可选——适用于已传入 URL 的调用;EasyUseAxiosReturn<T, R, D>(简易形态):execute的签名是(url: string, config?: AxiosRequestConfig<D>) => Promise<EasyUseAxiosReturn<T, R, D>>(useAxios.md),URL 必填——适用于未传 URL(useAxios(config?)、useAxios(instance?)、useAxios(config, instance))的调用。
两套接口共享的公共字段(useAxios.md)包括:
export interface UseAxiosReturn<
T,
R = AxiosResponse<T>,
_D = any,
O extends UseAxiosOptions = UseAxiosOptions<T>,
> {
/** 完整的 axios 响应对象 */
response: ShallowRef<R | undefined>
/** 响应数据;传入 initialData 时为 Ref<T>,否则为 Ref<T | undefined> */
data: O extends UseAxiosOptionsWithInitialData<T>
? Ref<T>
: Ref<T | undefined>
/** 请求是否已完成 */
isFinished: Ref<boolean>
/** 请求是否进行中 */
isLoading: Ref<boolean>
/** 请求是否被取消 */
isAborted: Ref<boolean>
/** 可能发生的任何错误 */
error: ShallowRef<unknown | undefined>
/** 中止当前请求 */
abort: (message?: string | undefined) => void
/** abort 的别名 */
cancel: (message?: string | undefined) => void
/** isAborted 的别名 */
isCanceled: Ref<boolean>
}
这里有几个值得注意的细节:
abort(message?)可以携带中止原因字符串,会被透传给 axios 的AbortController取消机制;data的类型是条件类型,由选项是否带initialData决定,这是「类型随配置自动收紧」的范本;response与error刻意使用ShallowRef,避免对大型响应体做深度响应化,与shallow: true默认值的设计取向一致。
十、在 airi 仓库中的实际落地建议
综合上文,在 airi 这类 pnpm workspace + Vue 3 多端仓库中使用 useAxios,落地路径大致如下:
- 确认依赖:在目标子包(如
apps/stage-web、packages/stage-shared)的package.json中声明@vueuse/integrations与axios。当前仓库公共依赖目录 pnpm-workspace.yaml 已含@vueuse/core(^14.4.0),版本基调与 VueUse 14 系列一致。 - 注意 axios 解析:仓库的 pnpm-lock.yaml 将
axios重映射为feaxios@^0.0.23,因此在仓库内使用useAxios前,应确认该 fork 实现与@vueuse/integrations的实例化、取消请求等接口兼容,必要时可在子包级覆盖回官方 axios。 - 遵循技能调用规则:按 SKILL.md 的
EXTERNAL规则,仅当项目已具备 axios 时才使用useAxios;若只需内置fetch,应优先考虑同属 Network 分类、无需外部依赖的useFetch(见 SKILL.md)。 - 按场景选择调用形态:接口层统一建
axios.create()实例承载拦截器;列表刷新用「仅传配置的execute」;初始化加载用await useAxios(...);重复触发场景开启abortPrevious防止响应乱序。
结语
useAxios 的价值在于把「请求生命周期」完整翻译成了 Vue 的响应式语言:data / error / isLoading / isFinished 四个 ref 覆盖了 UI 的全部状态需求,execute 与 abort 覆盖了交互侧的触发与取消,thenable 返回值则打通了命令式 await 与声明式响应式的边界。配合 .agents/skills/vueuse-functions/references/useAxios.md 中保留的完整类型声明,开发者可以在获得完整类型安全的同时,把 axios 接入成本压缩到最低。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00