Vue.js+Laravel:实现随数据加载状态动态控制的页面加载Spinner
解决Vue全局加载Spinner适配所有API请求的问题
核心方案
抛弃固定超时逻辑,通过全局追踪API请求数量控制Spinner显示:请求开始时计数+1,请求结束(成功/失败)时计数-1,当计数归0时自动隐藏Spinner。同时将Spinner组件改为受控组件,响应全局加载状态。
步骤1:重构PreloaderComponent
将组件改为接收外部加载状态的受控组件,移除内部超时逻辑,并优化样式确保覆盖全屏:
<template> <div class="preloader" v-if="isLoading"> <!-- 黑色Spinner,基于Bootstrap样式调整 --> <div class="spinner-border text-dark" style="width: 3rem; height: 3rem;" role="status"> <span class="visually-hidden">Loading...</span> </div> </div> </template> <script> export default { name: "PreloaderComponent", props: { isLoading: { type: Boolean, default: true } } } </script> <style lang="scss"> .preloader { position: fixed; /* 改为fixed,确保覆盖整个视口 */ width: 100vw; height: 100vh; left: 0; top: 0; z-index: 1050; background: rgba(0, 0, 0, .3); display: flex; align-items: center; justify-content: center; } </style>
步骤2:全局管理加载状态
以Vue3的Pinia为例(Vue2可替换为Vuex/事件总线),创建全局加载状态store:
// store/loading.js import { defineStore } from 'pinia' export const useLoadingStore = defineStore('loading', { state: () => ({ requestCount: 0, firstLoadTime: null }), getters: { isLoading: (state) => state.requestCount > 0 }, actions: { increment() { this.requestCount++ // 记录首次加载时间,用于控制最小显示时长 if (this.requestCount === 1) { this.firstLoadTime = Date.now() } }, async decrement() { if (this.requestCount > 0) { this.requestCount-- } // 确保Spinner至少显示300ms,避免闪显 if (this.requestCount === 0 && this.firstLoadTime) { const elapsed = Date.now() - this.firstLoadTime if (elapsed < 300) { await new Promise(resolve => setTimeout(resolve, 300 - elapsed)) } this.firstLoadTime = null } } } })
步骤3:封装API请求拦截器
基于Axios封装请求,自动追踪所有API请求的开始/结束:
// utils/api.js import axios from 'axios' import { useLoadingStore } from '@/store/loading' const api = axios.create({ baseURL: '/api' // 替换为你的API基础地址 }) // 请求拦截器:开始请求时增加计数 api.interceptors.request.use(config => { const loadingStore = useLoadingStore() loadingStore.increment() return config }, error => { const loadingStore = useLoadingStore() loadingStore.decrement() return Promise.reject(error) }) // 响应拦截器:请求结束(成功/失败)时减少计数 api.interceptors.response.use(response => { const loadingStore = useLoadingStore() loadingStore.decrement() return response }, error => { const loadingStore = useLoadingStore() loadingStore.decrement() return Promise.reject(error) }) export default api
步骤4:在Dashboard中使用Spinner
在Dashboard.vue中引入组件和store,绑定全局加载状态:
<template> <div class="dashboard"> <PreloaderComponent :isLoading="loadingStore.isLoading" /> <!-- 你的仪表盘组件内容 --> </div> </template> <script setup> import PreloaderComponent from '@/components/PreloaderComponent.vue' import { useLoadingStore } from '@/store/loading' const loadingStore = useLoadingStore() </script>
内容的提问来源于stack exchange,提问作者Anvita Rastogi
相关产品推荐
相关产品推荐

