全局Loading实现

20 阅读1分钟
import { customRef } from 'vue';

export const loading = customRef((track, trigger) => {
  let loadingCount = 0;
  return {
    get() {
      track();
      return loadingCount > 0;
    },
    set(value) {
      loadingCount += value ? 1 : -1;
      loadingCount = Math.max(loadingCount, 0);
      trigger();
    }
  }
})

export async function request(url: string, params: Record<string, string>) {
  const query = new URLSearchParams(params).toString();
  // 请求之前开启loading
  loading.value = true;
  return fetch(`${url}?${query}`).then((res) => res.json()).finally(() => {
    // 请求结束, 关闭 loading
    loading.value = false;
  })
}

原理:

  • 使用 Vue 的 customRef 创建了一个自定义的响应式引用
  • 内部维护了一个 loadingCount 计数器
  • get 方法返回 loadingCount > 0 的布尔值
  • set 方法根据传入的值增减计数器
  • 使用 track 和 trigger 实现响应式更新

特点:

  • 支持多个请求同时进行时的 loading 状态管理
  • 只有当所有请求都完成时,loading 才会变为 false

功能:

  • 封装了一个异步请求函数
  • 自动处理 URL 参数拼接
  • 自动管理 loading 状态

使用场景:

  • 在 Vue 应用中管理多个异步请求的加载状态
  • 适用于需要显示全局加载状态的场景
  • 可以避免多个请求同时进行时 loading 状态闪烁的问题

注意事项:

  1. loading 状态是基于计数器的,所以可以同时处理多个请求
  2. finally 确保无论请求成功还是失败,loading 状态都会被正确更新
  3. loadingCount 使用 Math.max 确保不会出现负数
  4. 这个实现特别适合需要统一管理加载状态的应用

参考: 远方os - customRef 实现全局 loading