结合axios + vue hook封装一个异步请求的useAsync方法

704 阅读2分钟

useAsync 的使用

直接查看具体使用方式

<script setup lang="ts">
    const {loading, data, error, run, cancel} = useAsync(api)
    /*
        分析:
        loading    加载状态
        data       成功结果
        error      失败结果
        run        发出请求
        cancel     取消请求
        参数:
        api        我们的异步请求
    */
    onMounted(() => {
        // requerstParams 请求参数
        /* requestConfig  AxiosRequestConfig额外的配置, 或者一些自定义的配置
        在axios的响应拦截可以获取到配置信息,自定义响应结果
        instance.interceptors.response.use((response) => {
            console.log(response.config)
        })
      
        */
        run(requerstParams, requestConfig)
    })
</script>

useAsync 具体代码实现

    const useAsync = <T = unknown>(
      api: (params: Recordable, config: RequestConfig) => Promise<T>
    ) => {
      const { initState, delay = 0 } = config
      const result = reactive<State<T>>({
        data: initState ? (initState as T) : undefined,
        loading: false,
        error: undefined,
      })
      const cancel = ref<undefined | (() => void)>(undefined)

      const run = async (pms: Recordable = {}, conf: Recordable = {}) => {
        result.loading = true
        try {
          const data = await api(pms), {
            cancelToken: new CancelToken(function executor(c) {
              cancel.value = c
            }),
            ...conf,
          })
          result.data = data as UnwrapRef<T>
          result.error = undefined
          return data
        } catch (err) {
          result.error = err
        } finally {
          result.loading = false
        }
      }
      return {
        ...toRefs(result),
        cancel,
        run,
      }
    }

到此useAsync的封装结束。

useAsync 的扩展 useQueryWithPagination

需求分析:相信大家用element-plus 以及 ant design vue不少,我个人比较喜欢用后者,一起来聊聊 Table 组件。想必我们使用table组件难免会遇到切换页码发出请求情况,接下来我们基于useAsync去封装一个,不过在此之前,我们还是先来看看他的使用。

useQueryWithPagination 的使用

<script setup lang="ts">
    // 搜索参数
    const filterParams = reactive({
      name: 'zhangsan',
      age: 20
    })
    const {data, loading,run, reload, onPageChange} = useQueryWithPagination(api)
    // 搜索
    const onFilterSearch = () => {
        run(filterParams)
    }
    // 重刷
    const onRelod = () => reload()
 </script>
 <template>
     <a-table :loading="loading" :data-source="data" columns="..." @change="onPageChange"/>
 </template>

useQueryWithPagination 具体代码实现

export const useQueryWithPagination = <T>(
  api: (params: Recordable, config: RequestConfig) => Promise<T>,
  params?: Recordable,
  config: Config = {},
) => {
  const { current = 1, pageSize = 10 } = config

  const pagination = reactive<PaginationType>({
    current: current,
    pageSize: pageSize,
    total: 0,
  })

  const { data, loading, error, run, cancel } = useAsync(api)
  
  const reload = () =>
    run({
      ...toRaw(params),
      page: pagination.current,
      size: pagination.pageSize,
    })

  const onPageChange = (value: PaginationType) => {
    Object.assign(pagination, value)
  }

  watchEffect((invalid) => {
    run({
      ...params,
      size: pagination.pageSize,
      page: pagination.current,
    })
    invalid(() => {
      cancel.value?.()
    })
  })

  return {
    loading,
    error,
    pagination,
    run,
    reload,
    data: data as UnwrapRef<T>,
    onPageChange,
  }
}

重点分析watchEffect

watchEffect((invalid) => {
    // 组件挂载之后调用,即onMounted
    run({
      ...params,
      size: pagination.pageSize,
      page: pagination.current,
    })
    // 依赖状态更新或者组件卸载之后调用,即onUpdated或者onUnmountd
    invalid(() => {
      // 取消请求
      cancel.value?.()
    })
  })

学过的React的同学,完全可以把它当作useEffect使用。

源码

axios的封装

useAsync的封装

具体使用案例

这个源码和示例有些出入,主要是添加一些配置值,整体思路差不多。

总结

本文只是介绍了两种hook async的封装方法,但是通常情况下还有一种就是我们进入详情页的时候会立即调用接口请求详情信息,所以你完全可以基于useAsync进行扩展,由于实现简单,我就不哆嗦了,就是加个onMounted的事情,具体实现可以查看源码。

如果喜欢文本,欢迎大家收藏点赞。