You need to enable JavaScript to run this app. Vue JS中如何将Spinner功能封装到服务层无需逐页引入组件
优惠活动
大模型
产品
解决方案
定价
更多

Vue JS中如何将Spinner功能封装到服务层无需逐页引入组件

实现方案

完全可以实现,核心思路是通过Vue的编程式渲染API将Spinner组件动态挂载到全局DOM节点上,不需要在页面/组件内显式引入,再把显隐方法暴露给你的Service类调用即可,不需要引入第三方加载库。

你可以先给Spinner的modal容器补充基础样式,确保加载动画居中、层级足够:

<template>
    <div class="spinner-modal" v-if="start">
          <div class="spinner-border text-info" role="status" style="width: 3rem; height: 3rem;">
            <span class="visually-hidden">Loading...</span>
          </div>
    </div>
</template>

<script>
export default {
    name: 'Spinner',
    props: {
        start: {
            type: Boolean,
            default: true,
        },
    },
}
</script>

<style scoped>
.spinner-modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background: rgba(255,255,255,0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999;
}
</style>

步骤1:封装全局Spinner控制工具

Vue3版本

新建spinnerService.js文件,代码如下:

// spinnerService.js
import { createApp } from 'vue'
import Spinner from './Spinner.vue' // 导入你修改好的Spinner组件

let spinnerInstance = null
let mountNode = null
// 并发请求计数器,避免多请求时加载动画提前消失
let requestCount = 0

export const showSpinner = () => {
  requestCount++
  if (spinnerInstance || requestCount > 1) return
  // 创建挂载节点
  mountNode = document.createElement('div')
  document.body.appendChild(mountNode)
  // 生成组件实例并挂载
  const app = createApp(Spinner, { start: true })
  spinnerInstance = app.mount(mountNode)
}

export const hideSpinner = () => {
  requestCount--
  if (requestCount > 0) return
  if (!spinnerInstance || !mountNode) {
    requestCount = 0
    return
  }
  // 卸载组件并销毁节点
  spinnerInstance.unmount()
  document.body.removeChild(mountNode)
  spinnerInstance = null
  mountNode = null
  requestCount = 0
}

Vue2版本

如果是Vue2项目,spinnerService.js调整为如下写法:

// spinnerService.js Vue2版本
import Vue from 'vue'
import Spinner from './Spinner.vue'

let spinnerInstance = null
let mountNode = null
let requestCount = 0

export const showSpinner = () => {
  requestCount++
  if (spinnerInstance || requestCount > 1) return
  const SpinnerConstructor = Vue.extend(Spinner)
  mountNode = document.createElement('div')
  document.body.appendChild(mountNode)
  spinnerInstance = new SpinnerConstructor({
    propsData: { start: true }
  }).$mount(mountNode)
}

export const hideSpinner = () => {
  requestCount--
  if (requestCount > 0) return
  if (!spinnerInstance || !mountNode) {
    requestCount = 0
    return
  }
  spinnerInstance.$destroy()
  document.body.removeChild(mountNode)
  spinnerInstance = null
  mountNode = null
  requestCount = 0
}

步骤2:在服务封装类中调用

改造你的Service请求封装,在请求前后调用对应方法即可,哪怕是路由beforeEnter钩子中调用的请求也能正常触发加载动画:

// 你的Service封装文件
import { showSpinner, hideSpinner } from './spinnerService'

const Service = {
  async getList() {
    showSpinner()
    try {
      // 保留你原有请求逻辑
      const res = await axios.get('/your/api/interface')
      return res.data
    } finally {
      // 无论请求成功/失败都隐藏加载动画
      hideSpinner()
    }
  },
  // 其余所有请求方法都可以按同样逻辑调用显隐方法
}

可选优化

可以给showSpinner增加300ms左右的延迟触发逻辑,如果请求耗时短于延迟时间则不显示加载动画,避免页面频繁出现闪烁的加载状态。

内容的提问来源于stack exchange,提问作者Laterneman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.08 19:57:00