Vue3 Axios企业级封装终章:手把手实现useRequest Hook,自动管理请求状态

更新日期: 2026-04-02 阅读: 5 标签: Axios

各位前端小伙伴,做Vue3项目时是不是总有这样的烦恼:明明已经封装了Axios,可每个组件里调接口,还是要反复写loading、error、data的状态管理,手动处理请求触发,甚至参数变了还要重新写请求逻辑?代码冗余不说,维护起来更是头大。

其实解决这个问题的核心,就是封装一个通用的useRequest Hook——把网络请求的通用逻辑全抽离,组件里一行代码就能搞定请求加状态管理。这篇文章作为Vue3 Axios企业级封装系列的终章,借鉴React ahooks中useRequest的设计理念,手把手实现一个适配Vue3的简化版useRequest,自动管理状态、支持依赖追踪、可手动触发,企业级项目直接拎走用。

在此之前,我们已经完成了Axios架构设计、Mock服务搭建、请求取消/防重/重试等核心功能,这套封装组合起来,能直接覆盖日常开发90%的网络请求需求。


一、为什么一定要封装Vue3的useRequest

网络请求的核心痛点,从来都不是调接口本身,而是请求状态的重复管理:

  • 每个请求都要定义loading(控制加载态)、error(捕获错误)、data(接收返回值),代码复制粘贴到吐

  • 组件挂载时要手动调请求,表单提交要单独写触发逻辑,逻辑分散

  • 当请求参数(如ID、页码)变化时,要手动监听并重新发起请求,多一步操作就多一分bug

而useRequest的核心价值,就是把这些通用逻辑抽离成独立的Hook,组件中只需一行代码引入,就能获取请求的所有状态和触发方法,彻底告别重复代码,让组件只专注于渲染逻辑。这也是Vue3组合式API的核心设计思想。


二、useRequest核心功能,一次满足所有需求

我们要实现的useRequest是简化版但够用版,核心实现4个企业级必备功能,轻量且实用:

  • 自动状态管理:内置loading/error/data响应式状态,直接解构使用,无需手动定义

  • 自动请求:组件挂载时可自动发起请求,无需手动调用

  • 手动触发:提供run方法,支持表单提交等手动触发请求的场景

  • 依赖追踪:监听响应式依赖变化,自动重新发起请求,适配参数动态变化的场景


三、手把手实现useRequest Hook,TS版可直接复用

整个实现过程分5步,基于TypeScript开发,类型提示拉满,目录结构遵循Vue3项目的企业级规范,直接复制到项目中就能用。

3.1 目录结构与文件创建

在项目的src/http/core目录下创建use-request.ts文件,作为useRequest Hook的核心实现文件;同时在src/http/core/index.ts中统一导出,方便组件引入。

text
src/
└── http/
    └── core/
        ├── use-request.ts  // useRequest核心实现
        └── index.ts        // 统一导出入口

3.2 定义配置项类型(UseRequestOptions)

首先定义useRequest的入参配置项类型,包含自动请求、依赖项、初始数据、各类回调函数:

import type { WatchSource } from 'vue'
import type { AxiosRequestConfig } from 'axios'

/**
 * useRequest配置项类型
 * @template T 请求返回数据的类型
 */
export interface UseRequestOptions<T> {
  // 是否在组件挂载时自动请求,默认true
  auto?: boolean
  // 响应式依赖项,变化时自动重新请求
  deps?: WatchSource<any>[]
  // 请求的初始数据
  initialData?: T
  // 请求前的回调函数
  onBefore?: () => void
  // 请求成功的回调函数,接收返回数据
  onSuccess?: (data: T) => void
  // 请求失败的回调函数,接收错误信息
  onError?: (error: any) => void
  // 请求完成的回调函数(无论成功/失败都会执行)
  onFinally?: () => void
}

3.3 定义返回结果类型(UseRequestReturn)

再定义useRequest的返回值类型,包含响应式状态和手动触发方法:

import type { Ref } from 'vue'
import type { AxiosRequestConfig } from 'axios'

/**
 * useRequest返回结果类型
 * @template T 请求返回数据的类型
 */
export interface UseRequestReturn<T> {
  // 请求返回的响应式数据
  data: Ref<T | undefined>
  // 请求加载状态,true表示加载中
  loading: Ref<boolean>
  // 请求错误信息,null表示无错误
  error: Ref<any | null>
  // 手动触发请求的方法,可传入Axios配置项
  run: (config?: AxiosRequestConfig) => Promise<T>
}

3.4 核心函数实现(useRequest)

这是最关键的一步,实现useRequest的核心逻辑,结合Vue3的ref和watch API,处理请求执行、状态更新、依赖监听:

import { ref, watch } from 'vue'
import type { AxiosRequestConfig } from 'axios'
import type { UseRequestOptions, UseRequestReturn } from './type'

/**
 * Vue3通用网络请求Hook
 * @template T 请求返回数据的类型
 * @param requestFn 异步请求函数,需返回Promise
 * @param options 配置项,默认空对象
 * @returns 包含请求状态和方法的对象
 */
export function useRequest<T>(
  requestFn: (config?: AxiosRequestConfig) => Promise<T>,
  options: UseRequestOptions<T> = {}
): UseRequestReturn<T> {
  const {
    auto = true,
    deps = [],
    initialData,
    onBefore,
    onSuccess,
    onError,
    onFinally
  } = options

  const data = ref<T | undefined>(initialData)
  const loading = ref(false)
  const error = ref<any | null>(null)

  const executeRequest = async (config?: AxiosRequestConfig): Promise<T> => {
    try {
      onBefore?.()
      loading.value = true
      error.value = null
      const result = await requestFn(config)
      data.value = result
      onSuccess?.(result)
      return result
    } catch (err) {
      error.value = err
      onError?.(err)
      throw err
    } finally {
      loading.value = false
      onFinally?.()
    }
  }

  const run = (config?: AxiosRequestConfig): Promise<T> => {
    return executeRequest(config)
  }

  if (auto) {
    executeRequest()
  }

  if (deps.length > 0) {
    watch(deps, () => {
      executeRequest()
    }, { deep: true })
  }

  return {
    data,
    loading,
    error,
    run
  }
}

3.5 统一导出Hook

在src/http/core/index.ts中导出useRequest,方便组件通过统一路径引入:

// 导出useRequest Hook及相关类型
export * from './use-request'


四、三大实战场景,快速上手用起来

实现完useRequest后,结合列表查询、详情获取、表单提交三个最常见的业务场景,教你如何快速使用。

场景1:基本使用 —— 组件挂载自动请求(列表查询)

适用于商品列表、用户列表等组件挂载即查询的场景。

<template>
  <div class="list-page">
    <div v-if="loading" class="loading">加载中...</div>
    <div v-else-if="error" class="error">请求失败:{{ error.message }}</div>
    <div v-else class="list">
      <ul>
        <li v-for="item in data?.list" :key="item.id">{{ item.title }}</li>
      </ul>
      <button @click="run" class="refresh-btn">刷新列表</button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { useRequest } from '@/http/core'
import { demoService } from '@/services/demo-service'
import type { PageData, Demo } from '@/services/demo-service'

const { data, loading, error, run } = useRequest<PageData<Demo>>(() => 
  demoService.getList({ pageNum: 1, pageSize: 10 })
)
</script>

场景2:依赖追踪 —— 参数变化自动请求(详情页)

适用于商品详情、用户详情等参数动态变化的场景。

<template>
  <div class="detail-page">
    <h1>Demo详情</h1>
    <select v-model="demoId">
      <option value="1">Demo 1</option>
      <option value="2">Demo 2</option>
      <option value="3">Demo 3</option>
    </select>

    <div v-if="loading">加载中...</div>
    <div v-else-if="error">请求失败:{{ error.message }}</div>
    <div v-else class="detail">
      <h2>{{ data?.title }}</h2>
      <p>{{ data?.content }}</p>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'
import { useRequest } from '@/http/core'
import { demoService } from '@/services/demo-service'
import type { Demo } from '@/services/demo-service'

const demoId = ref(1)
const demoIdRef = computed(() => demoId.value)

const { data, loading, error } = useRequest<Demo>(
  () => demoService.getDetail(Number(demoId.value)),
  { deps: [demoIdRef], auto: true }
)
</script>

场景3:手动触发 —— 禁用自动请求(表单提交)

适用于登录、注册、创建数据等表单提交场景。

<template>
  <div class="create-page">
    <h1>创建Demo</h1>
    <input v-model="title" placeholder="请输入标题" class="input" />
    <input v-model="content" placeholder="请输入内容" class="input" />
    <button @click="handleCreate" :disabled="loading" class="submit-btn">
      {{ loading ? '创建中...' : '立即创建' }}
    </button>

    <div v-if="error" class="error">创建失败:{{ error.message }}</div>
    <div v-if="data" class="success">创建成功:{{ data.title }}</div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { useRequest } from '@/http/core'
import { demoService } from '@/services/demo-service'
import type { Demo } from '@/services/demo-service'

const title = ref('')
const content = ref('')

const { data, loading, error, run } = useRequest<Demo>(
  (params) => demoService.create(params),
  { auto: false }
)

const handleCreate = async () => {
  if (!title.value || !content.value) return
  await run({
    title: title.value,
    content: content.value,
    author: 'test',
    status: true,
    createTime: new Date().getTime()
  })
  title.value = ''
  content.value = ''
}
</script>


五、封装总结与扩展方向

核心总结

  • useRequest的核心是抽离通用逻辑,将网络请求的状态管理、触发方式、依赖监听封装成独立Hook,让组件代码更简洁

  • 最佳实践:将API调用逻辑封装到服务层(如demo-service.ts),useRequest只负责调用服务层方法,实现关注点分离,便于后续API维护

  • 这套封装轻量且通用,基于Vue3组合式API和TypeScript开发,适配绝大多数企业级Vue3项目

扩展方向(按需实现)

本文实现的是简化版useRequest,大家可以根据项目需求,在此基础上扩展更多实用功能:

  • 请求缓存:缓存请求结果,相同参数不再重复请求,提升性能

  • 轮询请求:支持定时轮询,适用于实时数据刷新场景

  • 防抖节流:防止重复点击提交,适配高频请求场景

  • 取消请求:结合之前实现的Axios取消请求功能,支持手动取消正在进行的请求

  • 空数据处理:内置空数据状态,统一处理空数据渲染


最后

这套Vue3 Axios封装 + useRequest Hook的组合,是我在多个企业级项目中亲测好用的方案,从基础的Axios封装到高阶的Hook实现,一步一步解决了网络请求的所有痛点。

Vue3的组合式API最大的魅力,就是让我们能自由地抽离和复用逻辑,告别Vue2选项式API的束缚。希望这篇文章能帮你简化项目中的网络请求代码,提升开发效率。

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/13549

相关推荐

axios常见传参方式_axios中get/post/put/patch请求

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。axios中get/post/put/patch请求。传参格式为 formData ,传参格式为 query 形式 ,传参格式为 raw等

axios的特点与使用_解决处理axios兼容性问题

axios基于 Promise 的 HTTP 请求客户端,可同时在浏览器和 node.js 中使用。项目中发现,在安卓4.3及以下的手机不支持axios的使用,主要就是无法使用promise。加上以下polyfill就可以了。

vue中axios的使用与封装

分享下我自己的axios封装,axios是个很好用的插件,都是一些params对象,所以很方便做一些统一处理。当然首先是npm安装axios 很简单。在src下新建文件夹 service / index.js,接着上代码

axios处理Http请求的基本使用方法总汇

axios的功能特性:在浏览器中发送 XMLHttpRequests 请求,在 node.js 中发送 http请求,支持 Promise API,拦截请求和响应,转换请求和响应数据,自动转换 JSON 数据,客户端支持保护安全免受 XSRF 攻击

vue axios不缓存get请求(防止返回304不更新数据)

最近做项目遇到一款浏览器,由于缓存了get请求,导致不管如何刷新,数据都不更新的问题。以下分享一下解决办法:解决思路就是给每一条get请求增加一个timestamp的参数,value为时间戳

axios-mock-adapter_一个axios调试好用的工具

axios-mock-adapter可以用来拦截http请求,并模拟响应,使用起来也很简单,比如你想模拟下服务器返回个500错误,什么404找不到、403禁止访问、500服务器错误、503服务不可用、504网关超时等等,你都能模拟出来

Vue+Typescript中在Vue上挂载axios使用时报错

在vue项目开发过程中,为了方便在各个组件中调用axios,我们通常会在入口文件将axios挂载到vue原型身上,如下:这样的话,我们在各个组件中进行请求时

Vue项目中使用Axios封装http请求

使用axios可以统一做请求响应拦截,例如请求响应时我们拦截响应信息,判断状态码,从而弹出报错信息。请求超时的时候断开请求,还可以很方便地使用then或者catch来处理请求。

vue中axios请求的封装

发送请求模块目录,@/api/url中存放的是每个模块的URL,使用webpack提供的require.context将src/api/url下后缀为js的所有文件引入,并整理出一个对象。整合common.js & product.js,最终得到的对象如下:

使用axios请求,前端数字long类型精度问题解决方法

后台数据库定义的bigint类型(对应Long)的值太长会导致传递给前端的时候精度丢失,其原因是后端语言和js对字节码的解析长度不一样。前端js对Long类型支持的精度不够,导致后端使用的Long传到前端丢失精度。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!