欢迎光临优选殡葬网
详情描述

针对 Ajax 请求队列和全局加载状态,我提供一个基于 Axios 和 ElementUI 的完整解决方案:

一、核心实现方案

1. 请求队列管理器

// requestQueue.js
class RequestQueue {
  constructor() {
    this.queue = []
    this.pendingCount = 0
    this.maxConcurrent = 5 // 最大并发数
  }

  add(request) {
    return new Promise((resolve, reject) => {
      this.queue.push({
        request,
        resolve,
        reject
      })
      this.process()
    })
  }

  process() {
    if (this.pendingCount >= this.maxConcurrent || this.queue.length === 0) {
      return
    }

    const { request, resolve, reject } = this.queue.shift()
    this.pendingCount++

    request()
      .then(resolve)
      .catch(reject)
      .finally(() => {
        this.pendingCount--
        this.process()
      })
  }

  clear() {
    this.queue = []
    this.pendingCount = 0
  }
}

export const requestQueue = new RequestQueue()

2. Axios 拦截器封装

// request.js
import axios from 'axios'
import { Loading, Message } from 'element-ui'
import { requestQueue } from './requestQueue'

// 全局 loading 实例
let loadingInstance = null
let requestCount = 0

const showLoading = () => {
  if (requestCount === 0) {
    loadingInstance = Loading.service({
      lock: true,
      text: '加载中...',
      background: 'rgba(0, 0, 0, 0.7)',
      customClass: 'global-loading'
    })
  }
  requestCount++
}

const hideLoading = () => {
  requestCount--
  if (requestCount <= 0) {
    loadingInstance && loadingInstance.close()
    requestCount = 0
  }
}

// 创建 axios 实例
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API,
  timeout: 10000
})

// 请求拦截器
service.interceptors.request.use(
  config => {
    // 将请求加入队列
    const request = () => {
      showLoading()
      return service.request(config)
    }

    // 判断是否需要排队
    if (config.needQueue) {
      return requestQueue.add(request)
    }

    showLoading()
    return config
  },
  error => {
    hideLoading()
    return Promise.reject(error)
  }
)

// 响应拦截器
service.interceptors.response.use(
  response => {
    hideLoading()
    return response.data
  },
  error => {
    hideLoading()

    // 错误处理
    if (error.response) {
      switch (error.response.status) {
        case 401:
          Message.error('未授权,请重新登录')
          // 跳转登录页
          break
        case 403:
          Message.error('拒绝访问')
          break
        case 404:
          Message.error('请求地址不存在')
          break
        case 500:
          Message.error('服务器内部错误')
          break
        default:
          Message.error(error.message)
      }
    }

    return Promise.reject(error)
  }
)

// 请求方法封装
export const get = (url, params = {}, config = {}) => {
  return service({
    method: 'get',
    url,
    params,
    ...config
  })
}

export const post = (url, data = {}, config = {}) => {
  return service({
    method: 'post',
    url,
    data,
    ...config
  })
}

// 批量请求控制
export const batchRequest = (requests, maxConcurrent = 3) => {
  return new Promise((resolve, reject) => {
    const results = []
    let index = 0
    let completed = 0
    let hasError = false

    const run = async () => {
      if (index >= requests.length || hasError) return

      const currentIndex = index
      const request = requests[currentIndex]
      index++

      try {
        const result = await request()
        results[currentIndex] = result
        completed++

        if (completed === requests.length) {
          resolve(results)
        } else {
          run()
        }
      } catch (error) {
        hasError = true
        reject(error)
      }
    }

    // 启动初始请求
    for (let i = 0; i < Math.min(maxConcurrent, requests.length); i++) {
      run()
    }
  })
}

export default service

3. Vue 插件封装

// loadingPlugin.js
import { Loading } from 'element-ui'

export default {
  install(Vue) {
    Vue.prototype.$globalLoading = {
      show(text = '加载中...') {
        return Loading.service({
          lock: true,
          text,
          background: 'rgba(0, 0, 0, 0.7)'
        })
      },

      hide() {
        Loading.service().close()
      },

      async withLoading(promise, options = {}) {
        const loading = this.show(options.text)
        try {
          const result = await promise
          return result
        } finally {
          loading.close()
        }
      }
    }
  }
}

4. 在 main.js 中使用

import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import loadingPlugin from './plugins/loadingPlugin'
import { requestQueue } from './utils/requestQueue'

Vue.use(ElementUI)
Vue.use(loadingPlugin)

// 在 Vue 原型上挂载
Vue.prototype.$requestQueue = requestQueue

5. 组件中使用示例

<template>
  <div>
    <!-- 普通请求 -->
    <el-button @click="handleSingleRequest">
      单次请求
    </el-button>

    <!-- 批量请求 -->
    <el-button @click="handleBatchRequest">
      批量请求
    </el-button>

    <!-- 队列请求 -->
    <el-button @click="handleQueueRequest">
      队列请求
    </el-button>

    <!-- 带独立 loading 的请求 -->
    <el-button @click="handleWithLoading">
      独立Loading
    </el-button>
  </div>
</template>

<script>
import { get, post, batchRequest } from '@/utils/request'

export default {
  methods: {
    // 1. 普通请求(自动显示全局 loading)
    async handleSingleRequest() {
      try {
        const data = await get('/api/user/list')
        // 处理数据
      } catch (error) {
        console.error(error)
      }
    },

    // 2. 批量请求控制并发数
    async handleBatchRequest() {
      const requests = [
        () => get('/api/users/1'),
        () => get('/api/users/2'),
        () => get('/api/users/3'),
        () => get('/api/users/4'),
        () => get('/api/users/5')
      ]

      try {
        // 最多同时3个请求
        const results = await batchRequest(requests, 3)
        console.log('所有请求完成:', results)
      } catch (error) {
        console.error('批量请求失败:', error)
      }
    },

    // 3. 队列请求(排队执行)
    async handleQueueRequest() {
      const promises = []

      for (let i = 0; i < 10; i++) {
        promises.push(
          get(`/api/data/${i}`, null, { needQueue: true })
        )
      }

      try {
        const results = await Promise.all(promises)
        console.log('队列请求完成:', results)
      } catch (error) {
        console.error('队列请求失败:', error)
      }
    },

    // 4. 独立 loading 的请求
    async handleWithLoading() {
      try {
        const result = await this.$globalLoading.withLoading(
          get('/api/heavy-data'),
          { text: '正在加载大量数据...' }
        )
        // 处理结果
      } catch (error) {
        console.error(error)
      }
    },

    // 5. 手动控制全局 loading
    async handleManualLoading() {
      const loading = this.$globalLoading.show('自定义文本...')

      try {
        const data = await get('/api/data')
        // 处理数据
      } finally {
        loading.close()
      }
    }
  }
}
</script>

<style>
/* 自定义 loading 样式 */
.global-loading .el-loading-spinner {
  font-size: 16px;
}

.global-loading .el-loading-spinner .circular {
  width: 42px;
  height: 42px;
}
</style>

6. 高级功能扩展

// requestManager.js
export class RequestManager {
  constructor() {
    this.requests = new Map()
  }

  // 添加请求并返回取消函数
  add(key, request) {
    const source = axios.CancelToken.source()

    const wrappedRequest = request()
      .then(response => {
        this.requests.delete(key)
        return response
      })
      .catch(error => {
        this.requests.delete(key)
        throw error
      })

    this.requests.set(key, { 
      request: wrappedRequest, 
      cancel: source.cancel 
    })

    return wrappedRequest
  }

  // 取消特定请求
  cancel(key, message = '请求已取消') {
    if (this.requests.has(key)) {
      this.requests.get(key).cancel(message)
      this.requests.delete(key)
    }
  }

  // 取消所有请求
  cancelAll(message = '所有请求已取消') {
    this.requests.forEach(({ cancel }) => cancel(message))
    this.requests.clear()
  }

  // 检查是否有请求正在进行
  hasPending() {
    return this.requests.size > 0
  }
}

二、使用建议

普通请求:使用封装的 get/post 方法,自动显示全局 loading 批量请求:使用 batchRequest 控制并发数量 特殊场景:使用队列请求或独立 loading 取消请求:使用 RequestManager 管理可取消的请求

这个方案提供了完整的请求管理和加载状态控制,可以根据实际需求进行调整和扩展。

相关帖子
紧急避孕药半年最多可以吃几次?紧急避孕药什么时候服用有效
紧急避孕药半年最多可以吃几次?紧急避孕药什么时候服用有效
如果觉得2026年的救助金不够用,个人可以通过哪些正规渠道反馈诉求?
如果觉得2026年的救助金不够用,个人可以通过哪些正规渠道反馈诉求?
结婚时间很短就决定离婚,婚前支付的彩礼是否还能主张返还一部分?
结婚时间很短就决定离婚,婚前支付的彩礼是否还能主张返还一部分?
2026年全国各地使用电子社保卡乘坐公交地铁的优惠和方式是否已经统一?
2026年全国各地使用电子社保卡乘坐公交地铁的优惠和方式是否已经统一?
在合作便利店消费获得的积分,能否通用到加油抵扣中?
在合作便利店消费获得的积分,能否通用到加油抵扣中?
安阳市crm系统开发#java开源cms二次开发,提供一站式建站服务
安阳市crm系统开发#java开源cms二次开发,提供一站式建站服务
朔州市殡葬殡仪服务,白事服务公司,专业团队
朔州市殡葬殡仪服务,白事服务公司,专业团队
海西殡葬一条龙价格-白事告别会布置,专业的团队
海西殡葬一条龙价格-白事告别会布置,专业的团队
在预算有限的情况下,如何通过食材选择和烹饪方法提升日常饮食营养?
在预算有限的情况下,如何通过食材选择和烹饪方法提升日常饮食营养?
徐州市网站开发正规公司-购物网站开发,收费透明
徐州市网站开发正规公司-购物网站开发,收费透明
deepin没有swap分区怎么办 deepin没有swap分区解决方法
deepin没有swap分区怎么办 deepin没有swap分区解决方法
2026年关于非婚生子女的权利保护,法律上具体有哪些明确规定?
2026年关于非婚生子女的权利保护,法律上具体有哪些明确规定?
继承一个社交账号,继承人可能会承担哪些潜在的法律风险或义务?
继承一个社交账号,继承人可能会承担哪些潜在的法律风险或义务?
自由职业者或个体工商户如何为自己规划生育保障?
自由职业者或个体工商户如何为自己规划生育保障?
如何与独居老人进行有效沟通,了解他们的真实需求与困难?
如何与独居老人进行有效沟通,了解他们的真实需求与困难?
西双版纳短视频运营推广#网站运营服务,多年建站经验
西双版纳短视频运营推广#网站运营服务,多年建站经验
铜陵市殡葬电话|丧葬礼仪服务,丧礼布置
铜陵市殡葬电话|丧葬礼仪服务,丧礼布置
临汾市殡葬一条龙服务公司电话,丧事一条龙服务,一年365天
临汾市殡葬一条龙服务公司电话,丧事一条龙服务,一年365天
外出旅行住酒店或乘坐交通工具,忘记带实体证件该如何解决?
外出旅行住酒店或乘坐交通工具,忘记带实体证件该如何解决?
黄南商城网站建设-企业建站,优秀开发团队
黄南商城网站建设-企业建站,优秀开发团队