工具类-useLoading

useLoading

一个管理 loading 状态的工具。

执行 useLoading 返回:

  • loading: 一个 ComputedRef<boolean> 类型的变量,代表当前的 loading 状态
  • executor:执行器,用于执行需要 loading 的函数
  • closeLoading:用于手动关闭 loading
import { computed, ref } from 'vue';

type AsyncFunction = (...args: any[]) => Promise<any>;

/**
 * loading 状态管理
 * 自动管理函数执行的 loading 状态
 */
export function useLoading() {
  // 记录当前 loading 的数量
  const loadingCount = ref(0);
  const loading = computed(() => loadingCount.value > 0);

  const setLoading = (value: boolean) => {
    loadingCount.value = value ? loadingCount.value + 1 : Math.max(loadingCount.value - 1, 0);
  };

  /**
   * 执行器, 执行传入的函数, 并自动管理 loading 状态
   */
  const executor = async <T extends AsyncFunction>(
    execFunc: T,
    ...args: Parameters<T>
  ): Promise<ReturnType<T>> => {
    setLoading(true);
    try {
      const result = await execFunc(...args);
      return result;
    } finally {
      // 在 finally 中执行, 确保无论成功还是失败都会关闭 loading
      setLoading(false);
    }
  };

  /**
   * 手动关闭所有 loading
   */
  const closeLoading = () => {
    loadingCount.value = 0;
  };

  return { loading, executor, closeLoading };
}

ExecFunc 是执行函数的类型,使用泛型 P 表示执行函数的参数,泛型 T 表示执行函数的返回值。

使用示例

script

const func = async (name: string, age: number) => {
  await Promise.resolve();
  return { name, age }; 
}

const { loading, executor, closeLoading } = useLoading();

const handleLoading = async () => {
  const res = await executor(func, 'eno', 18);
  console.log(res); // {name: 'eno', age: 18}
}

template

<template>
  <div v-loading="loading" style="width: 100px; height: 100px"></div>
  <a-button @click="handleLoading()">loading</a-button>
  <a-button @click="closeLoading()">close loading</a-button>
</template>

为什么要使用 loadingCount 记录 loading 数量

useLoading 主要用于解决多次执行一个需要 loading 的函数时,先完成的函数会取消掉其他函数 loading 状态的问题

如果我们不用 loadingCount

function useLoading() {
  const loading = ref(false);

  // 执行器, 执行传入的函数, 并自动管理 loading 状态
  const executor = async <Promise<ReturnType<T>>>(
    execFunc: T,
    ...args: Parameters<T>
  ): Promise<ReturnType<T>> => {
    loading.value = true;
    try {
      const result = await execFunc(...args);
      return result;
    } finally {
      loading.value = false;
    }
  };

  // 手动关闭 loading
  const closeLoading = () => {
    loading.value = false;
  };

  return { loading, executor, closeLoading };
}

尝试执行两次执行 executor 函数,并且控制中间间隔一段时间

const func = async (name: string) => {
  await new Promise(resolve => {
    // 模拟 5 秒的延时异步操作
    setTimeout(() => {
      resolve('');
    }, 5 * 1e3);
  });
  console.log(name);
};

const { loading, executor, closeLoading } = useLoading();

const handleLoading = async () => {
  executor(func, 'eno');
  // 4 秒后在第一次未完成时,再次执行 executor 
  setTimeout(() => {
    executor(func, 'eno');
  }, 4 * 1e3);
};

尝试后的效果是:点击 loading 按钮,显示 loading,五秒后第一个执行函数完成,loading 关闭,但是此时第二个还行函数还未完成,这样显然不是我们想要的效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

eno_zeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值