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

大家好,我是`vue-hooks-plus`的作者,这里和大家分享一下属于Vue3的方便快捷的请求方式是怎么样的。

`Vue3` 现在已经越来越多小伙伴用上了,在很多技术群中对于`Vue3`的评价是很高的,在技术博客、群里也会见到很多小伙伴展示自己的 `Vue3` 代码块以及对 `Vue3` 项目搭建的一些心得。

相信大部分项目采用了 `Vite+Vue3`开发,结合网上的资源我发现对于项目整体大架构,比如 状态管理 `pinia`、开发构建 `Vite` 、UI 框架 `Element-plus` 、`Vant` 、`Antd-vue` 等主流框架,网络请求 `axios` 已经有很成熟的搭建使用方案了,咱们搞业务开发,基本上围绕着网络请求打交道,这里和大家聊聊不为人知的、好维护、质量可靠的业务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拿上一次 `请求参数` 的很方便啊! ✅。
    -   第二个,某个值变化了需要重新请求,这里很多小伙伴会想到我把这个请求封装起来,放进`watch`里面监听执行 ✅
    -   第三个,在封装的请求函数中加入判断条件是否执行 ✅

    `上述场景多个组件,多次重复操作,这样文件一多,也存在很多代码重复 ❌`

## ✅ 正确思路

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

什么是 `hook` ?

在 `React` 中的意思是将一个以 `use` 开头,含`react` 状态和 effect 纯函数的外部代码挂入到它的节点当中。

在 `Vue` 中,我认为将 `Vue` 的 effect ,如 ref、watch、watchEffect 的外部代码放入vue中执行,也是挂入 `hook`。

这里我们命名 `useRequest`

```
 const useRequest = (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写法,里面使用到了 `ref` 、`watch`,很多小伙伴从`Vue2`过渡来`Vue3`的,对这块这样使用的接触的很少。使用高质量可靠的请求`hook`我相信后面会成为`Vue3`开发的一个趋势,下面我展示一下业务中的使用流程,展现它的魅力 😉

```
<template>
  <div>读取值:{{ data }}</div>
</template>

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

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

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

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

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

### 🏃 Use

-   二次封装 `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` 类型,支持 `ts`

```
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 } = useRequest(()=>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 保姆级业务开发教程](https://inhiblab-core.gitee.io/docs/hooks/useRequest/guide/)

[useRequest 国内文档地址](https://inhiblab-core.gitee.io/docs/hooks/useRequest/)

[Github 地址](https://github.com/InhiblabCore/vue-hooks-plus/tree/master/packages/hooks/src/useRequest)

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

## ✨ 文档闪亮登场

![iShot_2022-10-27_10.04.30.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3c5d4dbd239b488791fefeeaf03c22fb~tplv-k3u1fbpfcp-zoom-1.image)

![iShot_2022-10-27_10.03.35.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9918dd4c0aac4d5ba31dae70756d9267~tplv-k3u1fbpfcp-zoom-1.image)

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值