Skip to content

useRequest

该 hook 用于处理请求,包括请求的发送、请求的状态、请求的结果等。

实现源码
ts
import { ref, shallowRef, computed, watch } from 'vue';

import type { Ref, ShallowRef } from 'vue';

export interface IRequestOptions {
  defaultPage?: number;
  defaultPageSize?: number;
  /**
   * page或pageSize变化后,是否立即请求数据
   */
  immediate?: boolean;
}
export type IRequestFn<T, Q> = (query: Q) => Promise<{ data: T[]; total: number }>;
interface IRequestReturn<Q> {
  total: Ref<number>;
  loading: Ref<boolean>;
  page: Ref<number>;
  pageSize: Ref<number>;
  lastPage: Ref<number>;
  fetchData: (query?: Q) => void;
}

export function useRequest<T, Q extends Record<string, any> = {}>(
  request: IRequestFn<T, Q>,
  options?: IRequestOptions
): {
  list: Ref<T[]>;
} & IRequestReturn<Q>;

export function useRequest<T, Q extends Record<string, any> = {}>(
  request: IRequestFn<T, Q>,
  options: IRequestOptions,
  isShallow: true
): {
  list: ShallowRef<T[]>;
} & IRequestReturn<Q>;

export function useRequest<T, Q extends Record<string, any> = {}>(
  request: IRequestFn<T, Q>,
  {
    defaultPage = 1,
    defaultPageSize = 10,
    immediate = false
  }: IRequestOptions = {},
  isShallow = false
) {
  const list: Ref<T[]> | ShallowRef<T[]> = !isShallow
    ? ref([])
    : shallowRef([]);
  const loading = ref(false);
  const total = ref(0);
  const page = ref(defaultPage);
  const pageSize = ref(defaultPageSize);

  const lastPage = computed(() => {
    if (!total.value || !pageSize.value) {
      return 0;
    }

    return Math.ceil(total.value / pageSize.value);
  });

  const fetchData = (query?: Q) => {
    const params = {
      page: page.value,
      pageSize: pageSize.value,
      ...query
    } as unknown as Q;
    loading.value = true;

    request(params)
      .then((res) => {
        list.value = res.data;
        total.value = res.total;
      })
      .finally(() => {
        loading.value = false;
      });
  };

  if (immediate) {
    watch(
      [page, pageSize],
      () => {
        fetchData();
      },
      { immediate: true }
    );
  }

  return {
    list,
    total,
    loading,
    page,
    pageSize,
    lastPage,
    fetchData
  };
}

介绍

本文 useRequest 可以用来快速创建分页请求。该函数接收三个参数:

  • request 请求函数,返回值为Promise<{ data: T[]; total: number }>
  • options 可选参数,包含三个可选属性:
    1. defaultPageSize 默认分页大小,默认值为10
    2. defaultPage 默认当前页,默认值为1
    3. immediate 页码或每页大小变化时是否立即发起请求,默认值为false
  • isShallow 是否使用shallowRef对 data 进行响应式,默认值为false

返回值为一个对象,包含以下属性:

  • list 请求结果;
  • total 请求结果总数;
  • page 当前页码;
  • pageSize 每页大小;
  • loading 请求状态;
  • lastPage 最后一页页码;
  • fetchData 发起请求函数;

使用

vue
<template>
  <a-table
    class="ant-table-striped"
    :columns="columns"
    :data-source="list"
    :loading="loading">
    ...
  </a-table>

  <a-pagination
    v-model:current="page"
    v-model:page-size="pageSize"
    :total="total" />
</template>

<script setup>
import { useRequest } from 'useRequest.ts';

const columns = [...];
const { page, pageSize, total, loading, list } = useRequest(
  (params: { page: number; pageSize: number; key?: string; }) => {
    return new Promise<{ total: number; data: any[] }>((resolve) => {
      setTimeout(() => {
        resolve({
          data: [],
          total: 0
        })
      }, 1000);
    })
  },
  {
    immediate: true
  }
);
</script>

Released under the MIT License.