找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2412

积分

1

好友

333

主题
发表于 2025-12-30 16:32:25 | 查看: 24| 回复: 0

在 Vue 项目中,管理多个异步操作的 Loading 状态常常令人头疼。

一个页面如果需要调用三个接口,你是否还在这样写?

const loading1 = ref(false)
const loading2 = ref(false)
const loading3 = ref(false)
// 然后三个地方分别控制

这种方式过于原始。要么导致全屏 Loading 频繁闪烁,要么在页面上留下多个独立的 Loading 状态,影响体验。

本文将介绍一个在实际项目中验证过的方案,它基于 Pinia 实现,核心代码约百行,却能显著提升 Loading 管理的优雅度与健壮性。

核心思路:组件上报状态,Store 统一调度

设想这样一个场景:每个组件只需管理自身的 Loading 状态,并向一个全局 Store “上报”自己的加载情况。由 Store 统一监听所有组件的状态,只要还有任意一个组件在加载,就维持全屏 Loading 的展示。

这种设计带来了几个明显的好处:

  • 组件逻辑保持简洁,无需关心其他部分的加载状态。
  • 多个并发请求会被自动合并,只展示一次全局 Loading。
  • 组件卸载时能自动清理状态,避免潜在的内存泄漏问题。

代码实现:如何构建全局 Loading Store

首先来看 Store 部分的实现,这是整个方案的核心。

// loading-store.js
export const useLoadingStore = defineStore('loading', () => {
    // 存储所有组件的 loading 状态源
    const loadingSources = ref([])

    // 组件使用此方法注册自身
    const addLoadingSource = (source) => {
        loadingSources.value.push(source)
    }

    // 组件卸载时移除自身
    const removeLoadingSource = (source) => {
        const index = loadingSources.value.indexOf(source)
        if (index > -1) {
            loadingSources.value.splice(index, 1)
        }
    }

    // 关键计算属性:只要有一个状态源为真,即表示正在加载
    const isLoading = computed(() =>
        loadingSources.value.some(source => toValue(source))
    )

    // 监听加载状态变化,自动控制全屏 Loading 的显示与隐藏
    watch(isLoading, (loading) => {
        if (loading) {
            // 例如:显示 Element Plus 的全屏 Loading
            ElLoading.service({ fullscreen: true })
        } else {
            // 所有加载均完成,关闭全屏 Loading
            // 此处需处理 Loading 实例的引用,示例中简化处理
        }
    })

    return { addLoadingSource, removeLoadingSource }
})

在组件中如何使用

在组件中使用此方案变得极其简单,这得益于 Vue.js 的组合式 API 与 Pinia 的便捷性。

<script setup>
import { ref, onBeforeUnmount } from 'vue'
import { useLoadingStore } from './loading-store'

const loadingStore = useLoadingStore()
const myLoading = ref(false) // 组件自身的 loading 状态

// 向全局 Store 注册,告知“请监听我的状态”
loadingStore.addLoadingSource(myLoading)

const fetchData = async () => {
    myLoading.value = true // 只需操作自身的状态
    try {
        await api.getData()
    } finally {
        myLoading.value = false // 完成后关闭
    }
}

// 重要:组件卸载时务必清理注册的状态源
onBeforeUnmount(() => {
    loadingStore.removeLoadingSource(myLoading)
})
</script>

可以看到,组件完全无需关心全局 Loading 如何显示或隐藏,只需专注于管理自己的 myLoading.value = true/false。其余所有协调工作都由全局 Store 负责。

方案的优势与精妙之处

  1. 自动合并并发请求
    当页面同时发起三个请求时,三个组件会将各自的 Loading 状态设为 true,但全局全屏 Loading 只会出现一次。直到最后一个请求结束,Loading 才会消失,有效避免了闪烁。

  2. 组件卸载自动清理
    为了避免组件销毁后其 Loading 状态仍残留于 Store 中,我们必须在 onBeforeUnmount 生命周期钩子中移除注册。这是保证内存安全的重要习惯。

  3. 支持多样化的状态源
    示例中使用了 VueUse 提供的 toValue 工具函数,它能智能处理 refcomputed、普通值或 getter 函数等多种响应式数据源。这意味着你的 Loading 状态可以非常灵活:

    // 以下形式均可被识别
    const loading = ref(false)
    const loading = computed(() => someCondition)
    const loading = () => isLoading.value
  4. 强大的可扩展性
    如果你需要为 Loading 添加最小显示时长(防止闪烁),或者希望区分“页面级 Loading”和“局部 Loading”,只需在全局 Store 中增加相应的逻辑即可,所有使用该方案的组件都无需修改。

总结

这套基于 Pinia 的全局 Loading 管理方案,通过“分散上报,集中调度”的设计理念,巧妙地解决了 Vue 应用中多异步状态协调的难题。它使组件代码更纯粹,并提供了良好的自动合并与内存管理机制,具有很强的实用性与可扩展性,非常适合在中大型 Vue3 项目中应用。




上一篇:JavaScript性能优化:警惕JSON.parse(JSON.stringify())深拷贝的性能陷阱
下一篇:暗网商业化销售NtKiller工具:可绕过主流杀毒软件与企业级EDR
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-12 02:46 , Processed in 0.214146 second(s), 38 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表