Vue3全局配置Loading的完整指南:从基础到实战
Vue3全局配置Loading的完整指南:从基础到实战
引言:
在现代Web应用开发中,加载状态的管理直接影响用户体验。本文将系统介绍Vue3中实现全局Loading的三种主流方案,包括基于Pinia的状态管理方案、全局API挂载方案以及使用Ant Design Vue组件库的集成方案,并提供详细的实现步骤和最佳实践。
为什么需要全局Loading?
- 用户体验优化:避免用户重复操作和等待焦虑
- 操作状态统一:在复杂业务流程中保持一致的加载状态
- 代码复用:减少重复开发,提高维护效率
- 错误边界处理:网络异常时提供清晰的状态反馈
实现方案对比
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
Pinia状态管理 | 状态可控性强,支持复杂场景 | 实现相对复杂 | 中大型应用、状态复杂的场景 |
全局API挂载 | 轻量灵活,使用简单 | 缺乏状态管理能力 | 小型应用、快速原型开发 |
第三方UI库 | 风格统一,功能完善 | 增加依赖体积 | 已使用UI库的项目 |
方案一:基于Pinia的全局Loading实现
1. 创建Loading状态管理
// src/store/modules/loading.ts
import { defineStore } from 'pinia'export const useLoadingStore = defineStore('loading', {state: () => ({isLoading: false,count: 0 // 用于处理并发请求}),actions: {showLoading() {this.count++this.isLoading = true},hideLoading() {if (this.count > 0) {this.count--if (this.count === 0) {this.isLoading = false}}}}
})
2. 注册Pinia并创建全局组件
// src/main.ts
import { createPinia } from 'pinia'
const pinia = createPinia()
app.use(pinia)// src/components/GlobalLoading.vue
<template><div v-if="isLoading" class="loading-overlay"><div class="spinner"></div><p class="loading-text">{{ loadingText }}</p></div>
</template><script setup>
import { useLoadingStore } from '@/store/modules/loading'
import { computed } from 'vue'const loadingStore = useLoadingStore()
const isLoading = computed(() => loadingStore.isLoading)
const loadingText = computed(() => loadingStore.text || '加载中...')
</script><style scoped>
.loading-overlay {position: fixed;top: 0;left: 0;width: 100%;height: 100%;background: rgba(0, 0, 0, 0.5);display: flex;flex-direction: column;justify-content: center;align-items: center;z-index: 9999;
}.spinner {width: 50px;height: 50px;border: 5px solid #f3f3f3;border-top: 5px solid #3498db;border-radius: 50%;animation: spin 1s linear infinite;
}@keyframes spin {0% { transform: rotate(0deg); }100% { transform: rotate(360deg); }
}.loading-text {color: white;margin-top: 1rem;font-size: 1.2rem;
}
</style>
3. 在App.vue中引入
<template><router-view /><GlobalLoading />
</template><script setup>
import GlobalLoading from '@/components/GlobalLoading.vue'
</script>
4. 在HTTP请求中自动使用
// src/utils/http.ts
import { useLoadingStore } from '@/store/modules/loading'// 创建axios实例
const service = axios.create({baseURL: import.meta.env.VITE_API_URL,timeout: 5000
})// 请求拦截器
service.interceptors.request.use((config) => {const requestConfig = config as RequestConfig;// 二次封装的请求参数if (requestConfig?.showLoading) {const loadingStore = useLoadingStore()loadingStore.showLoading()}return config},(error) => {const loadingStore = useLoadingStore()loadingStore.hideLoading()return Promise.reject(error)}
)// 响应拦截器
service.interceptors.response.use((response) => {const loadingStore = useLoadingStore()loadingStore.hideLoading()return response},(error) => {const loadingStore = useLoadingStore()loadingStore.hideLoading()return Promise.reject(error)}
)
方案二:全局API挂载实现
1. 创建Loading服务
// src/utils/loading.ts
import { createVNode, render, App } from 'vue'
import LoadingComponent from './Loading.vue'let loadingInstance: any = null
const container = document.createElement('div')export const LoadingService = {install(app: App) {// 注册全局方法app.config.globalProperties.$loading = {show: (options = {}) => {if (loadingInstance) {this.hide()}const vnode = createVNode(LoadingComponent, options)render(vnode, container)document.body.appendChild(container.firstElementChild!)loadingInstance = vnode.component},hide: () => {if (loadingInstance) {render(null, container)loadingInstance = null}}}}
}
2. 在组件中使用
// src/views/Register/index.vue
<script setup lang="ts">
import { getCurrentInstance } from 'vue'const { proxy } = getCurrentInstance()!const handleSubmit = async () => {try {proxy.$loading.show({text: '注册中,请稍候...'})await registerUser(formData)proxy.$router.push('/login')} catch (error) {console.error('注册失败', error)} finally {proxy.$loading.hide()}
}
</script>
方案三:使用Ant Design Vue实现(UI库)
1. 安装依赖
npm install ant-design-vue @ant-design/icons-vue --save
2. 全局配置
import { createApp } from 'vue'
import App from './App.vue'
import { Spin } from 'ant-design-vue'
import { LoadingOutlined } from '@ant-design/icons-vue'
import 'ant-design-vue/dist/reset.css'const app = createApp(App)// 注册组件
app.component(Spin.name, Spin)
app.component(LoadingOutlined.name, LoadingOutlined)// 挂载全局Loading方法
app.config.globalProperties.$loading = {show: (options = {}) => {const container = document.createElement('div')document.body.appendChild(container)const loadingInstance = createVNode(Spin, {size: 'large',spinning: true,tip: options.tip || '加载中...',indicator: createVNode(LoadingOutlined, { spin: true }),style: {display: 'flex',alignItems: 'center',justifyContent: 'center',position: 'fixed',top: 0,left: 0,width: '100%',height: '100%',background: 'rgba(0, 0, 0, 0.5)',zIndex: 9999,...options.style}})render(loadingInstance, container)return () => {render(null, container)document.body.removeChild(container)}}
}app.mount('#app')
3. 结合HTTP拦截器使用
import { getCurrentInstance } from 'vue'const { proxy } = getCurrentInstance()!// 请求拦截器
service.interceptors.request.use((config) => {config.loadingHide = proxy.$loading.show({tip: '数据加载中...'})return config},(error) => {return Promise.reject(error)}
)// 响应拦截器
service.interceptors.response.use((response) => {response.config.loadingHide()return response},(error) => {error.config.loadingHide()return Promise.reject(error)}
)
三种方案对比与选择建议
Pinia方案
✅ 优点:状态管理清晰,支持复杂场景,适合大型应用
❌ 缺点:需要引入Pinia,配置相对复杂 💡 适用场景:中大型企业级应用,需要精细控制加载状态
全局API方案
✅ 优点:轻量级,无依赖,实现简单
❌ 缺点:状态管理较弱,不适合复杂场景 💡 适用场景:小型应用,快速原型开发
Ant Design Vue方案
✅ 优点:UI一致性好,功能完善,自带主题
❌ 缺点:增加第三方依赖体积 💡 适用场景:已使用Ant Design Vue的项目
高级特性扩展
1. 加载状态防抖
// 在loading.ts中添加
show: (options = {}) => {// 设置最小显示时间,避免闪烁const { minDuration = 300 } = optionsconst startTime = Date.now()// ... 原有代码 ...return () => {const endTime = Date.now()const duration = endTime - startTimeif (duration < minDuration) {setTimeout(() => {render(null, container)loadingInstance = null}, minDuration - duration)} else {render(null, container)loadingInstance = null}}
}
2. 支持多实例和局部加载
// 创建局部加载方法
showLocal: (target: HTMLElement, options = {}) => {const vnode = createVNode(LoadingComponent, options)render(vnode, container)target.appendChild(container.firstElementChild!)return () => {render(null, container)}
}
总结
全局Loading作为提升用户体验的关键功能,在Vue3中有多种实现方式。选择方案时应根据项目规模、团队技术栈和业务需求综合考量。小型项目可选择轻量级的全局API方案,中大型项目推荐使用Pinia方案以获得更好的状态管理能力,而已使用UI库的项目则应优先考虑集成方案以保持风格统一。
通过本文介绍的方法,你可以构建出灵活、高效且用户友好的全局加载状态管理系统,为你的Vue3应用提供专业级的用户体验。
如果你有更优的办法,可以评论区探讨一下!^v^