You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在未知提前传参时合理使用useAxios?优化TS服务实现

优化基于Vue组合式函数的TypeScript服务实现

现有实现回顾

你目前基于Vue组合式函数和DummyJSON后端构建了Todo服务,核心步骤如下:

1. 配置Axios基础URL

// src/axios/index.ts
import axios from 'axios'
axios.defaults.baseURL = 'https://dummyjson.com'

2. 全局导入Axios配置

// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import './axios'

const app = createApp(App)
app.mount('#app')

3. 初始Todo服务实现

// src/composables/services/todo.service.ts
import { useAxios } from '@vueuse/integrations/useAxios'

const BASE_URL = '/todos'

const useTodoService = {
  getAll: () => {
    return useAxios(`${BASE_URL}`) // 默认GET请求,立即执行
  }
}

4. 组件中调用服务(立即加载)

<!-- src/views/TestView.vue -->
<script setup lang="ts">
import useTodoService from '@/composables/services/todo.service'
const { data, isLoading } = useTodoService.getAll()
</script>

<template>
  <div>Loading: {{ isLoading }}</div>
  <div>First todo: {{ data?.todos[0] }}</div>
</template>

5. 改为点击触发请求

更新服务:

// src/composables/services/todo.service.ts
const useTodoService = {
  getAll: () => {
    return useAxios(
      `${BASE_URL}`,
      {},
      { immediate: false }
    )
  }
}

组件中调用:

<!-- src/views/TestView.vue -->
<script setup lang="ts">
import useTodoService from '@/composables/services/todo.service'
const { data, isLoading, execute } = useTodoService.getAll()
</script>

<template>
  <div>Loading: {{ isLoading }}</div>
  <div>First todo: {{ data?.todos[0] }}</div>
  <button @click="execute">Fire!</button>
</template>

6. 带参数的POST请求实现

更新服务(自定义execute):

// src/composables/services/todo.service.ts
const useTodoService = {
  getAll: () => {
    // 与之前代码一致
  },
  add: () => {
    const useAxiosObj = useAxios(
      `${BASE_URL}/add`,
      { method: 'POST' },
      { immediate: false }
    )

    const execute = (title: string) => {
      return useAxiosObj.execute({ data: { todo: title, completed: false, userId: 5 } })
    }

    return { ...useAxiosObj, execute }
  }
}

组件中调用:

<!-- src/views/TestView.vue -->
<script setup lang="ts">
import useTodoService from '@/composables/services/todo.service'
const { data, isLoading, execute } = useTodoService.add()

function onClick() {
  execute('Lorem ipsum...')
}
</script>

<template>
  <div>Loading: {{ isLoading }}</div>
  <div>Added todo: {{ data }}</div>
  <button @click="onClick">Fire!</button>
</template>

现有实现的问题

  • 自定义execute函数不符合useAxios的标准API,熟悉该工具的开发者会产生认知偏差
  • 扩展useAxios返回对象时触发IDE警告:Expected a non-Promise value to be spreaded in an object
  • 重复代码多,抽象层构建繁琐,难以统一处理请求/响应逻辑

优化方案

1. 封装通用请求组合式函数

创建useApi统一处理请求配置、响应转换和基础逻辑,避免重复代码:

// src/composables/useApi.ts
import { computed } from 'vue'
import { useAxios } from '@vueuse/integrations/useAxios'
import type { AxiosRequestConfig, UseAxiosOptions } from '@vueuse/integrations/useAxios'

export function useApi<T = any>(
  url: string,
  config?: AxiosRequestConfig,
  options?: Omit<UseAxiosOptions<T>, 'immediate'>
) {
  const { data, isLoading, error, execute, ...rest } = useAxios<T>(
    url,
    config,
    { immediate: false, ...options }
  )

  // 统一提取响应数据(适配DummyJSON的返回结构)
  const transformedData = computed(() => data.value?.data ?? null)

  // 自定义请求触发函数,支持传入业务参数
  const request = async (params?: { params?: any; data?: any }) => {
    const requestConfig: AxiosRequestConfig = {
      ...config,
      ...(params?.params && { params: params.params }),
      ...(params?.data && { data: params.data })
    }
    return execute(requestConfig)
  }

  return {
    data: transformedData,
    isLoading,
    error,
    request,
    execute, // 保留原execute方法,兼容原有使用习惯
    ...rest
  }
}

2. 重构Todo服务为规范的组合式函数

改用函数返回业务方法的结构,避免对象包裹,同时封装业务逻辑:

// src/composables/services/useTodoService.ts
import { useApi } from '../useApi'

const BASE_URL = '/todos'

export function useTodoService() {
  // 获取所有Todo(立即执行)
  const getAll = () => {
    return useApi(`${BASE_URL}`, { method: 'GET' }, { immediate: true })
  }

  // 添加Todo(手动触发)
  const useAddTodo = () => {
    const { data, isLoading, request } = useApi(`${BASE_URL}/add`, { method: 'POST' })

    // 业务化的添加方法,对外暴露简洁参数
    const addTodo = async (title: string) => {
      // 这里统一处理业务参数转换
      return request({
        data: {
          todo: title,
          completed: false,
          userId: 5
        }
      })
    }

    return {
      data,
      isLoading,
      addTodo,
      request // 保留底层请求方法,留扩展空间
    }
  }

  return {
    getAll,
    useAddTodo
  }
}

3. 组件中使用优化后的服务

<!-- src/views/TestView.vue -->
<script setup lang="ts">
import { useTodoService } from '@/composables/services/useTodoService'

const { getAll, useAddTodo } = useTodoService()

// 自动加载所有Todo
const { data: todosData, isLoading: todosLoading } = getAll()

// 处理添加Todo逻辑
const { data: addedTodo, isLoading: addLoading, addTodo } = useAddTodo()

const handleAddTodo = async () => {
  await addTodo('Lorem ipsum...')
}
</script>

<template>
  <div>加载Todos: {{ todosLoading }}</div>
  <div>第一条Todo: {{ todosData?.todos[0] }}</div>

  <div>添加Todo状态: {{ addLoading }}</div>
  <div>已添加Todo: {{ addedTodo }}</div>
  <button @click="handleAddTodo">添加Todo</button>
</template>

优化后的优势

  • 完全遵循Vue组合式函数规范,结构清晰易维护
  • 通用请求逻辑集中在useApi,避免重复代码,便于统一修改
  • 业务服务对外暴露简洁的业务方法,同时保留底层API的扩展性
  • 解决IDE警告:不再直接扩展包含Promise的useAxios返回对象,通过组合方式构建返回值
  • 统一处理响应数据转换,无需在每个服务中重复实现

内容的提问来源于stack exchange,提问作者PyKKe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 15:28:12