针对 Ajax 请求队列和全局加载状态,我提供一个基于 Axios 和 ElementUI 的完整解决方案:
// 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()
// 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
// 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()
}
}
}
}
}
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
<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>
// 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 管理可取消的请求
这个方案提供了完整的请求管理和加载状态控制,可以根据实际需求进行调整和扩展。