都在说Hooks,快封装一个属于自己的useRequest的吧~

3,579 阅读5分钟

Hi~,我是一碗周,如果写的文章有幸可以得到你的青睐,万分有幸~

🍇 写在前面

现在Vue3已经全面拥抱Hooks,如果还不了解或者是什么,赶紧去学学吧,关于Hooks春哥有一篇文章写的很不错:浅谈:为啥vue和react都选择了Hooks🏂?这篇文章已经讲解的非常好了;

Vue3也有一个比较不错的Hooks的库,也就是VueUse,这个里面有非常多的Hooks,文档的例子也非常的友好,非常建议学习一下。

前一段时间写了一篇封装axios的文章,这篇文章可以说是上篇文章的一个续集,基于封装的axios上在进行一次封装,方便请求。

封装的useRequest已经实现的功能如下:

  • 防抖和节流

  • 轮询

  • 自动请求和手动请求

这篇文章的源代码全部可以再Github上找到。

🍈 为什么要封装

在实际的开发中,很多请求都需要做一些重复的事情,比如loading、防抖、接口请求结束的提示等等,这些操作基本都是重复的,配合Hooks将这些操作进行封装大大减少了重复代码。

还有就是如果团队使用同一的方式进行请求,如果后期需要对接口请求做一些统一的处理也会更加方便。

🍉 需要的参数

既然要封装,首先需要我们将参数确认一下:

  • 第一个参数是一个异步请求的函数,这里使用之前封装的`request,你也可以根据自己的需求修改为url或者两者都可以;

  • 第二个参数是请求的参数;

  • 第三个参数就是关于useRequest的一些配置信息了,主要包含以下几项:

    属性类型描述默认值
    debounceboolean是否开启防抖false
    debounceIntervalnumber防抖的时间,单位毫秒1000
    throttleboolean是否开启节流false
    throttleIntervalnumber节流的时间,单位毫秒1000
    pollingboolean是否开启轮询false
    pollingIntervalnumber轮询的时间,单位毫秒5000
    autoRunboolean是否自动调用true
    onFinish(data: T) => void接口执行完毕调用的函数undefined

现在我们将基本的参数定义好了,开始进行封装。

🍊 类型定义

由于我们使用TS进行封装,避免不了要定义一些类型,首先我们定义第三个参数的类型以及useRequest的返回值类型:

import type { Ref } from 'vue'
export interface IUseRequestOption<T = any> {
  // 是否开启防抖 时长
  debounce?: boolean
  debounceInterval?: number
  // 是否开启节流 时长
  throttle?: boolean
  throttleInterval?: number
  // 是否轮询
  polling?: boolean
  pollingInterval?: number
  // 是否自动调用
  autoRun?: boolean
  // 调用完毕可执行的函数
  onFinish?: (data: T) => void
}
export interface IUseRequestRequest<D, T> {
  loading: Ref<boolean>
  data: Ref<T | undefined>
  run: (...args: any[]) => Promise<void> // 手动请求方法
  runParams: (params: D) => Promise<void> // 带参数的请求方法
}

然后我们定义useRequest的基本类型,示例代码如下:

import type { IUseRequestOption, IUseRequestRequest } from './../type'
// 引入返回值类型
import type { YWZResponse } from '/@/service/index'
const useRequest = < // 泛型
  ParamType = any, // 参数的类型
  PromiseRequestType = any, //  返回的data的类型
  DataType = YWZResponse<PromiseRequestType>, // 返回的data的类型的外层
>(
  PromiseRequest: (p: ParamType) => Promise<DataType>, // 异步请求函数
  params: ParamType, // 参数
  opt?: IUseRequestOption<DataType>, // 配置项
): IUseRequestRequest<ParamType, DataType> => {}

export default useRequest

现在我们就可以开始进行编写主要代码了

🍋 开始封装

🍌 处理默认参数

默认参数我们通过Object.assign()方法实现,我们只需要定义一个默认的对象然后进行覆盖即可,示例代码如下:

const defaultOption: IUseRequestOption = {
  // 是否开启防抖 时长
  debounce: false,
  debounceInterval: 1000,
  // 是否开启节流 时长
  throttle: false,
  throttleInterval: 1000,
  // 是否轮询
  polling: false,
  pollingInterval: 5000,
  // 是否自动调用
  autoRun: true,
  // 调用完毕可执行的函数
  onFinish: undefined,
}
const useRequest = <
  ParamType = any,
  PromiseRequestType = any,
  DataType = YWZResponse<PromiseRequestType>,
>(
  PromiseRequest: (p: ParamType) => Promise<DataType>,
  params: ParamType,
  opt?: IUseRequestOption<DataType>,
): IUseRequestRequest<ParamType, DataType> => {
  type Params = ParamType
  // 合并配置项
  const option = Object.assign({}, defaultOption, opt)
  const loading = ref(false)
  const data = ref<DataType>()
  return {
    run: undefined,
    loading,
    data,
    runParams: undefined,
  }
}

🥭 调用和轮询

现在我们封装一下手动调用、自动调用和轮询的功能,这一部分比较简单,先说一下思路吧:

  1. loading设置为true

  2. 调用请求方法,并参数参数;

  3. loading设置为false并调用onFinish

  4. 如果是轮询则在几秒后递归调用。

实现代码如下:

// 调用方法
const run = async (): Promise<void> => {
  loading.value = true
  // 调用请求方法
  data.value = await PromiseRequest(params)

  loading.value = false
  option.onFinish && option.onFinish(data.value)
}
const runParams = async (_params: ParamType): Promise<void> => {
  loading.value = true
  // 调用请求方法
  data.value = await PromiseRequest(_params)

  loading.value = false
  option.onFinish && option.onFinish(data.value)
}
// 轮询
const polling = async () => {
  loading.value = true
  data.value = await PromiseRequest(params)
  loading.value = false
  option.onFinish && option.onFinish(data.value)
  delay(polling, option.pollingInterval as number)
}
// 自动调用
option.autoRun && run()
// 是否轮询
option.polling && polling()

delay方法是lodash中的一个方法,除此之外,下面用到的防抖和节流也是lodash中的方法。

🍎 防抖和节流

防抖和节流我们可以通过计算属性来返回经过防抖或者节流处理过的方法,实现也比较简单,代码如下:

// 计算最终使用的函数
const runComputed = computed(() => {
  // 判断是否开启防抖
  if (option.debounce)
    return {
      run: debounce(run, option.throttleInterval) as () => Promise<void>,
      runParams: debounce(runParams, option.throttleInterval) as (
        p: Params,
      ) => Promise<void>,
    }
  // 判断是否开启节流
  if (option.throttle)
    return {
      run: throttle(run, option.throttleInterval) as () => Promise<void>,
      runParams: throttle(runParams, option.throttleInterval) as (
        p: Params,
      ) => Promise<void>,
    }
  return { run, runParams }
})

最后,我们只要将将处理后的函数进行return即可,示例代码如下:

return {
  run: runComputed.value.run,
  loading,
  data,
  runParams: runComputed.value.runParams,
}

完整代码可以去Github上进行查看。

🍑 测试

现在我们可以对封装的这个Hooks进行测试,测试代码如下:

image_n9AT_-3AWh.png

这个测试代码在Github,你可以换一个API自己试试,看看效果是否跟想象的一样。

关于参数我这里说一下,如果使用方法,想要修改参数后手动调用 API创建的,不然每次调用都会使用默认的参数进行请求。

🍓 写在最后

本篇文章到这为止就结束了,如果你感觉这篇文章对你有用,可以点赞收藏支持一下(点赞的都脱单、暴富);上面的代码全部都在Github中,可以点个star避免迷路。

  • 如果文中有错误欢迎指正~

  • 如果你有建议欢迎提出~

最后说一下这个代码还有一个优化的点,就是当参数发生变化后,重新发送请求,实现也比较简单,就是监听params的变化,重新发送请求即可。