You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 09:55:25