likes
comments
collection
share

Vue3开发,或许你需要这样使用请求API

作者站长头像
站长
· 阅读数 34

大家好,我是vue-hooks-plus的作者,这里和大家分享一下属于Vue3的方便快捷的请求方式是怎么样的。Vue3 现在已经越来越多小伙伴用上了,在很多技术群中对于Vue3的评价是很高的。在技术博客、群里也会见到很多小伙伴展示自己的 vue3 代码块以及对于vue3项目搭建的一些心得,我自己项目也采用了 vite+vue3开发,结合网上的资源我发现对于项目整体大架构,比如 状态管理 pinia、开发构建 vite 、UI 框架 element-plusvantantd-vue 等主流框架,网络请求 axios 已经有很成熟的搭建使用方案了。咱们搞业务开发,基本上围绕着网络请求打交道,这里和大家分享如何在vue3书写出好维护、质量可靠的业务API请求。

📄 这里列出我所知的请求使用的方案

  • 二次封装axios、文件夹统一管理API请求,返回一个Promise,在组件中使用链式调用
  • 二次封装axios、文件夹统一管理API请求,返回一个Promise,在组件中使用 async/await 使用
  • 二次封装axios、全局注入axios,组件内直接调用中这个实例去传参数调用
  • 直接使用 axios 请求(打了重复繁杂的代码,这里只做举例,不推荐 ❌)

我相信很多小伙伴都属于以上的使用方案,我会逐个和大家分析,谈一下关于 vue3 网络请求这块的见解。

分析

二次封装 Axios

const axiosInstance = axios.create({
  baseURL: import.meta.env.VITE_SCREEN_BASE_URL,
  timeout: 1_000 * 10
});
// ... 省略拦截器

管理API的文件夹

export  function getData(params,options){
  return new Promise((resolve, reject) => {
    axiosInstance({
      url,
      params,
      ...options,
    })
      .then((res) => {
        resolve(res?.data?.data);
      })
      .catch((err) => {
        reject(err);
      });
  }
}

链式调用,比较传统

const data = ref()
​
getData({...xxx},{...xxx}).then((res)=>{
  // ...这里处理数据
  data.value = res.data
}).catch(err=>{
  // ...xxx
})
​

Async/Await 调用,比较优雅

 const data = ref()
 onMounted(async () => {
 const res = await getData({...x},{...x})
 data.value = res
 }

Provide/Inject 调用,和链式用法基本一致

main.ts
import axios from 'axios'
import VueAxios from 'vue-axios'
​
app.use(VueAxios, axios)
app.provide('axios', app.config.globalProperties.axios) 
​
App.vue
const axios: any = inject('axios')  // inject axios
axios({url,data,...其他配置}).then() // 同上

看法

我相信很多小伙伴会比较喜欢第二种的async/await 的语法糖,比较优雅,简洁。在当今业务越来越复杂的环境下,我们往往依赖很多东西,比如

  • 多次请求拿上一次的数据作为参数体。

  • 某个值变化了又需要重新请求。

  • 或者某个值满足什么条件才开始请求。

    对于链式调用来说,是非常不利于代码阅读的,整个业务组件充斥着重复代码,非常不利于维护。

    对于async/await 似乎很好的解决了以上的问题,有同学说 ,await拿上一次的很方便啊,我还能加判断条件让它是否要请求,还能监听依赖再继续请求,这里我们会发现,async/await 比链式优雅太多了,代码量也少了很多,但是,你总不可能这一个文件又加判断条件让它是否要请求,另外一个文件又需要依赖请求,这样文件一多,也存在很多代码重复。

    🙋 这里我们可以把依赖请求,判断条件是否发起请求这些封装成起来,有执行 async/await 的方法,又有这些依赖请求的功能模块一起联合使用,而用户只需要传递需要开启的功能配置即可;

    确实,我们代码需要复用,需要逻辑复用

    const useAxios = (service,option)=>{
      const data = ref()
      
      const run = async ()=>{
         const res = await service()
         data.value = res
      }
      
      // 是否满足条件
      if(option.ready){
        run()
      }
        // 依赖重新请求
      watch(option.deps,()=>{
        run()
      })
      
      return {
        data,
        run
      }
    }
    

    以上的写法是hook写法,也是我今天倡导大家使用的。我敢说,很多同学从vue2过渡来vue3的,对这块这样使用的接触的很少。使用高质量可靠的请求hook我相信后面会成为vue3开发的一个趋势,下面我展示一下业务中的使用流程,展现它的魅力 😉

<template>
  <div>读取值:{{ data }}</div>
</template><script lang="ts" setup>
import { ref } from 'vue'
import { useAxios } from '..'
 //、、、、、业务代码 
 //、、、、、业务代码 
const { data } = useAxios(() => getData(), {
  ready:true
  deps:[依赖其他响应式对象]
})
</script>

比如这块业务代码,用户直接拿到处理好的请求的data,它是一个响应式,所以业务层省去了一个声明响应式对象来保存数据的代码,ready判断条件,业务层又被砍去一个条件判断函数,依赖刷新,砍去书写watch的代码,用户只需要关注配置即可,我们会发现它是通过run方法调用触发请求的,那么,我们的防抖、节流函数还不是相加就加吗,所以这样的做法,是不是很简洁。

这一块的拓展性极强,你可以拓展 loading状态、params等出去。因为返回一个promise对象即可,所以意味着axios 和 request 这些都兼容,完全将请求这块的功能性的模块从业务层脱离,用户只需要关注配置和API接口!

const { data } = useAxios(() => getData(), { 
// 防抖
// 节流
// ready
// 依赖刷新
// 格式化请求
 ...
})

几行配置下来,给人的感觉就清爽,而且维护起来那叫一个舒服。

使用

  • 二次封装 axios 或者 其他请求工具
  • 封装请求 hook 钩子
  • 使用文件管理 API 接口,👆写的使用一个函数返回一个 promise 即可
  • 业务层使用 hook 传配置即可

额外tip

封装带有ts提示的axios axiosInstance 和大家常用的一样,AxiosRequestConfig 类型从 axios 导出。 注意: 因为这里没写拦截器,所以返回了一个多余的new Promise 让大家直观的看到,只需要接受返回的一个 Promise 对象即可,实际上不管是 axios 还是 request 本身返回的都是 Promsie 对象 的。

const request = <ResponseType = unknown>(
  url: string,
  options?: AxiosRequestConfig<unknown>,
): Promise<ResponseType> => {
  return new Promise((resolve, reject) => {
    axiosInstance({
      url,
      ...options,
    })
      .then((res) => {
        resolve(res?.data?.data);
      })
      .catch((err) => {
        reject(err);
      });
  });
};

需要配合拦截器截取 res.data.data

const request = <ResponseType = unknown>(
  url: string,
  options?: AxiosRequestConfig<unknown>,
): Promise<ResponseType> => {
  return  axiosInstance({url,...options}) 
};

请求函数示例 AnalysisReportType 为 数据返回预定的 data 类型

export async function getListReports(
  params?: {
    reportGroup?: string | null;
    sortCol?: 'visitUv' | 'uploadFileTime';
    sortType?: string;
    reportName?: string;
  },
) {
  return request<AnalysisReportType>('/platform/report/listReports', {
    params: { ...params, sortType: 2 },
  });
}

业务组件中

const { data } = useAxios(()=>getListReports(),{
  ready:true,
  ...其他配置
})

拿来吧你

由于vueuse没有这块的功能,我针对业务层写了一个 vue-hooks-plus库,完整的测试用例覆盖,高质量可靠,里面涵盖封装了一个 useRequest 钩子,自己公司已经用上,安全可靠,满足业务 99% 需求,并且支持按需引入,以下展示基本功能,更多详细见API文档 👇

const {
  loading: Ref<boolean>,
  data?: Ref<TData>,
  error?: Ref<Error>,
  params: Ref<TParams || []>,
  run: (...params: TParams) => void,
  runAsync: (...params: TParams) => Promise<TData>,
  refresh: () => void,
  refreshAsync: () => Promise<TData>,
  mutate: (data?: TData | ((oldData?: TData) => (TData | undefined))) => void,
  cancel: () => void,
} = useRequest<TData, TParams>(
  service: (...args: TParams) => Promise<TData>,
  {
    manual?: boolean,
    defaultParams?: TParams,
    formatResult?:(response:TData)=>any,
    onBefore?: (params: TParams) => void,
    onSuccess?: (data: TData, params: TParams) => void,
    onError?: (e: Error, params: TParams) => void,
    onFinally?: (params: TParams, data?: TData, e?: Error) => void,
  }
);

⚠️ 重点

作者整理出来一整套业务开发规范,快人一步的学会吧

useRequest 保姆级业务开发教程

useRequest 文档地址

Github 地址

喜欢的小伙伴顺手点个 star 🌟 ,多多支持!

文档截图 👇 配套设施完善!

Vue3开发,或许你需要这样使用请求API

Vue3开发,或许你需要这样使用请求API

目前已经建立了反馈群,感兴趣的小伙伴可以加入!

Vue3开发,或许你需要这样使用请求API