Vue项目里的Loading状态管理:一个基于Pinia的简洁方案
做Vue项目的时候,大家肯定都碰到过loading状态管理这个麻烦事。一个页面要调三个接口,你怎么写loading?像下面这样?
const loading1 = ref(false)
const loading2 = ref(false)
const loading3 = ref(false)然后三个地方分别控制。这种做法太土了。要么全屏loading闪来闪去,要么页面上一堆loading各管各的,看起来乱得很。
今天说一个我们项目里正在用的方案。用了Pinia,也就一百行代码,体验好很多。
核心思路
让组件自己上报,让store统一调度。
具体来说:每个组件只管自己的loading状态,然后告诉全局store说"我在加载"。store负责监听所有组件的状态,只要还有一个组件在加载,就保持全屏loading。
这样做有几个好处:
组件逻辑干净,不用关心别人是不是也在加载
多个请求自动合并成一个loading展示
组件卸载时自动清理,不会内存泄漏
代码实现
先看store部分,核心代码也就几十行:
// loading-store.js
import { defineStore } from 'pinia'
import { ref, computed, watch, toValue } from 'vue'
import { ElLoading } from 'element-plus'
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)
}
}
// 只要有一个组件在loading,就返回true
const isLoading = computed(() =>
loadingSources.value.some(source => toValue(source))
)
let loadingInstance = null
// 监听变化,自动显示或隐藏
watch(isLoading, (loading) => {
if (loading) {
// 显示全屏loading
if (!loadingInstance) {
loadingInstance = ElLoading.service({ fullscreen: true })
}
} else {
// 所有加载都完成了,关闭
if (loadingInstance) {
loadingInstance.close()
loadingInstance = null
}
}
})
return { addLoadingSource, removeLoadingSource }
})再看组件里怎么用:
<script setup>
import { ref } from 'vue'
import { 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全帮你搞定。
这个方案好在哪
自动合并请求
比如页面同时发三个请求,三个组件都把loading设为true,但全屏loading只出现一次。最后一个请求结束,loading才消失。
组件卸载自动清理
组件销毁了,它的loading状态还留在store里怎么办?在onBeforeUnmount里移除注册就行,这是个好习惯。
支持各种loading形式
上面用了toValue,这是VueUse里的工具函数,能处理ref、computed、普通值和getter函数。所以你的loading状态可以这样写:
// 都可以
const loading = ref(false)
const loading = computed(() => someCondition)
const loading = () => isLoading.value可扩展性强
如果想加个最小显示时间(防止loading一闪而过),或者想区分页面loading和局部loading,在store里加逻辑就行,组件完全不用改。
总结
这个方案的核心就是让组件只关心自己的事,全局的事交给store。代码不多,但用起来顺手,推荐你试试。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!