vue3 + ts 封装实现一个钩子 useRequest

204 阅读1分钟

使用ts在vue3项目中实现一个钩子:useRequest

目标

封装一个处理get请求的钩子,维护单独的数据,loading,(分页)

步骤

……本来打算写细分的,奈何写作功力一般,直接贴完整代码了,水平一般,代码里都有注释,有问题或者改进的地方可以评论区讨论

import { message } from "ant-design-vue";
import { computed, reactive, unref, watch } from "vue"

type TResult = any
type TPagination = Partial<IPagination>

// 配置
interface IOptions {
  saveVal: (result: TResult) => void; // 保存请求回的数据结构
  handleErr: (err: any) => void; // 请求的错误处理
  rightCode: number; // 请求正确的 code 码
  errMessage: boolean; // 请求发生错误是否 message 提示
  withPagination?: boolean; // get 请求是否携带分页参数
  getParams: any; // 请求参数
}

interface IPagination {
  pageSize: number;
  current: number;
  total: number;
  hideOnSinglePage: boolean;
}

interface IState {
  loading: boolean;
  data: Array<any>;
  pagination: TPagination;
}

const DEFAULT_VAL = {
  pageSize: 10,
  current: 1,
  saveVal: (result: TResult) => result.data.content || [],
  handleErr: (result: TResult) => message.error(result.msg),
  rightCode: 200,
  withPagination: true
}

/**
 * 封装网络请求
 * @param requestFun 请求函数
 * @param options 配置项
 * @returns 
 */
export const useRequest = (requestFun: (...args: any[]) => any, options: Partial<IOptions> & TPagination = {}) => {
  const state = reactive<IState>({
    loading: false,
    data: [],
    pagination: {
      pageSize: options.pageSize || DEFAULT_VAL.pageSize,
      current: options.current || DEFAULT_VAL.current,
      total: 0,
      hideOnSinglePage: ('hideOnSinglePage' in options) ? options.hideOnSinglePage : true
    }
  })
  const withPaginationRequestParams = computed(() => {
    return (Object.hasOwn(options, 'withPagination') ? options.withPagination : DEFAULT_VAL.withPagination) ? {
      ...unref(options.getParams) || {},
      pageNum: state.pagination.current,
      pageSize: state.pagination.pageSize,
    } : {
      ...unref(options.getParams) || {},
    }
  })

  async function fetchData(...params: any) {
    state.loading = true;
    try {
      // 如果传入 params 的话,使用 params ,否则使用 withPaginationRequestParams 当作请求参数
      const result = await (params.length ? requestFun(...params) : requestFun(withPaginationRequestParams.value));
      if (result.code === (options.rightCode || DEFAULT_VAL.rightCode)) {
        state.pagination.total = result.data?.totalSize || 0;
        state.data = options.saveVal ? options.saveVal(result) : DEFAULT_VAL.saveVal(result);
      } else {
        // 如果需要 message 提示,单独进行 错误msg message提醒
        options.errMessage && message.error(result.msg)
        return Promise.reject();
      }
    } catch (err) {
      if (options.handleErr) {
        options.handleErr(err);
      } else {
        DEFAULT_VAL.handleErr(err)
      }
    } finally {
      state.loading = false;
    }
  }

  // 重置分页信息
  function resetPagination() {
    state.pagination.pageSize = DEFAULT_VAL.pageSize;
    state.pagination.current = DEFAULT_VAL.current;
  }

  async function handleTableChange(pagination: TPagination) {
    state.pagination.pageSize = pagination.pageSize;
    state.pagination.current = pagination.current;
  }

  watch(() => [state.pagination.pageSize, state.pagination.current], () => {
    fetchData()
  })

  return {
    state,
    withPaginationRequestParams,

    // func
    fetchData,
    resetPagination,
    handleTableChange,
  }
}