只会使用VueRequest?封装简易功能的useRequest

1,245 阅读2分钟

vue3中的hooks其实是函数的写法,就是把一些单独功能的js代码抽离出来,放到单独的js文件中。最近在用vue3+ts开发项目,在实际的开发中需要做一些重复的事情,常常被 loading 状态的管理、请求的节流防抖、接口数据的缓存、分页等这些重复的实现所困惑。每当开启一个新项目时,我们都得手动去处理以上这些问题,这将是一个重复性的工作,而且还得确保团队的一致。封装useRequest:管理接口的状态。

封装的useRequest实现了以下的功能:
  1. 防抖和节流
  2. 轮询
  3. loading状态
  4. 手动请求和自动请求

image.png

useRequest.ts

import { ref, computed } from 'vue'
import { delay, debounce, throttle } from 'lodash'
import type { IUseRequestOption, IUseRequestRequest } from './../type'
// 引入返回值类型
import type { YWZResponse } from '@/service/index'
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>()
  // 警告
  if (option.throttle && option.debounce) {
    console.warn(
      '[ywz warn]: useRequest的配置项中的throttle和debounce均为true,请选择一个,否则这样默认使用防抖',
    )
  }

  // 调用方法
  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()
  // 计算最终使用的函数
  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 {
    run: runComputed.value.run,
    loading,
    data,
    runParams: runComputed.value.runParams,
  }
}
export default useRequest

type/index.ts

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>
}
export interface IWhyRequest<T> {
  code: number
  data: T
}

request/index.ts

import useRequest from './src/useRequest'
export { useRequest }

使用方法:

image.png