useLoading
一个管理 loading 状态的工具。
执行 useLoading 返回:
- loading: 一个 ComputedRef<boolean>类型的变量,代表当前的 loading 状态
- executor:执行器,用于执行需要 loading 的函数
- closeLoading:用于手动关闭 loading
import { computed, ref } from 'vue';
// 执行函数类型
type ExecFunc<P extends Array<any> = any[], T = any> = (...args: P) => Promise<T>;
// 执行函数参数
type ExecFuncParams<P extends Array<any> = any[]> = Parameters<ExecFunc<P>>;
/**
 * 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 <P extends Array<any> = any[], T = any>(
    execFunc: ExecFunc<P, T>,
    ...args: ExecFuncParams<P>
  ): Promise<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 <P extends Array<any> = any[], T = any>(
    execFunc: ExecFunc<P, T>,
    ...args: ExecFuncParams<P>
  ): Promise<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 关闭,但是此时第二个还行函数还未完成,这样显然不是我们想要的效果。



















