log
码中赤兔

Vue 项目中如何优雅地管理 Loading 状态

发布于 2024年12月9日
更新于 2025年1月30日
9 分钟阅读
Vue

在前后端通信时,为了提升用户体验,通常会显示一个 loading 状态,以告知用户当前的通信进度。然而,项目中可能有多个地方需要进行 Axios 通信,如果在每一次请求前后都显式地添加 loading 逻辑,代码将变得冗余且难以维护。

为了解决这个问题,我设计了一个通用的 Loading Hook 组件,基于 Element PlusAxios,可以大幅简化代码并提高复用性。以下是详细的实现和使用方法。


实现思路

通过一个 Hook,将 loading 逻辑集中管理,并提供以下功能:

  • 控制普通加载状态。
  • 支持多个异步请求的统一管理。
  • 支持全屏加载效果,适用于需要全局遮罩的场景。

Hook 代码实现

import { ref } from 'vue'
import { ElLoading } from 'element-plus'

type Options = {
  /** 初始值 */
  initialValue?: boolean
}

export function useLoading({ initialValue = false }: Options = {}) {
  /** 是否正在加载 */
  const isLoading = ref(initialValue)

  /**
   * 开始加载
   */
  const startLoading = () => {
    isLoading.value = true
  }

  /**
   * 结束加载
   */
  const stopLoading = () => {
    isLoading.value = false
  }

  /**
   * 处理多个异步请求
   * @param promises 异步请求列表
   * @returns 所有异步请求的结果
   */
  const withLoading = async <T>(promises: Array<() => Promise<T>>) => {
    startLoading()
    try {
      return await Promise.all(promises.map((promise) => promise()))
    } finally {
      stopLoading()
    }
  }

  /**
   * 处理多个异步请求,全屏加载
   * @param promises 异步请求列表
   * @returns 所有异步请求的结果
   */
  const withFullscreenLoading = async <T>(promises: Array<() => Promise<T>>) => {
    startLoading()
    const loading = ElLoading.service({ fullscreen: true })
    try {
      return await Promise.all(promises.map((promise) => promise()))
    } finally {
      stopLoading()
      loading.close()
    }
  }

  return { isLoading, startLoading, stopLoading, withLoading, withFullscreenLoading }
}

使用方法

以下是一个完整的示例,展示了如何在 Vue 页面中使用 useLoading Hook 来管理加载状态。

<template>
  <div class="wrapper">
    <button @click="clickHandler">Fetch Japanese Holidays</button>
    <hr />
    <p>{{ JSON.stringify(holidayList) }}</p>
  </div>
</template>

<script setup lang="ts">
import { useLoading } from '@/hooks/use-loading.hook'
import axios from 'axios'
import { ref } from 'vue'

// 使用 useLoading Hook
const { withFullscreenLoading } = useLoading()
const holidayList = ref<any>({})

// 请求逻辑封装
const requestJapaneseHoliday = async () => {
  const resp = await axios.get('https://holidays-jp.github.io/api/v1/date.json')
  return resp.data
}

// 按钮点击事件
const clickHandler = async () => {
  const [data] = await withFullscreenLoading([requestJapaneseHoliday])
  holidayList.value = data
}
</script>

优势分析

  1. 解耦代码逻辑,把 loading 状态的管理提取到 Hook 中,避免每次请求都重复写 start/stop 逻辑,代码更清晰、简洁。
  2. 支持多请求并发,无论是单个还是多个异步请求,都可以通过withLoadingwithFullscreenLoading轻松管理。
  3. 全屏加载支持,使用 Element Plus 的 ElLoading,快速实现全局遮罩,提升页面的视觉效果。
  4. 灵活的复用性,只需一次定义,就可以在整个项目中便捷调用,极大地提升了开发效率。

总结

通过封装useLoading Hook,我们可以优雅地管理 Axios 通信中的加载状态,既简化了代码,又增强了复用性。在实际项目中,这样的通用性设计不仅能减少开发工作量,还能提高代码的可维护性。

希望这个方案对你的项目有所帮助!

关于

分享技术见解、经验和思考的个人博客

联系方式

  • Email: hushukang_blog@proton.me
  • GitHub

© 2025 码中赤兔. 版权所有