业务需求
在发送请求的过程中显示加载状态,在全局进行应用,发送请求前将ant-design-vue的spin组件的状态设置为true,发送完之后将状态设置为false。
误区及难点
本打算在axios的请求拦截器和响应拦截器做处理,使用pinia状态管理进行spin状态的改变的记录。但忽略了一个页面中可能有多个请求,每次发生一个请求就开启关闭,单个请求响应时间短,多个请求没办法知道什么时候所有请求结束,页面上显示不出来加载状态。
解决方法
这里做了一个请求池,在发送请求时记录请求,响应结束将请求从请求池里面移除。然后监听请求池的状态,当请求池中的请求为0时,变更store里面spin的状态为false。
页面布局
<script setup lang="ts">
import { ref } from 'vue'
import {useCommonStore } from '@/store'
import { Spin } from 'ant-design-vue'//这里用的是按需引入
const commonStore = useCommonStore()
</script>
<template>
<Spin :spinning="commonStore.spinning">
<div
class="w-screen h-screen p-t-60px bg-[#f8f9fd]"
>
<div class="bg-[#fff] p-[10px]">
<RouterView />
</div>
</div>
</Spin>
</template>
store文件
import { defineStore } from 'pinia'
export const useCommonStore = defineStore('common-store', {
state: (): CommonState => ({
spinning: false,
}),
actions: {
setCurRequestNum(num: number) {
this.spinning = num !== 0//控制spin组件的状态
}
},
})
axios二次封装
import axios, { AxiosResponse } from 'axios'
import { getToken } from '../util/index.js'
import {useCommonStore } from '@/store'
import envConfig from '../../config'
const pendingMap = new Map()//请求池,存储请求
const service = axios.create({
baseURL: envConfig.api,
})
//获取请求的信息,进行记录
const getPendingKey=(config:any)=>{
let { url, method, params, data } = config
if (typeof data === 'string') data = JSON.parse(data || '{}') // response里面返回的config.data是个字符串对象
return [url, method, JSON.stringify(params), JSON.stringify(data || {})].join('&')//将url请求路径,method方法,params参数,data参数以&连接起来进行
}
//添加信息到请求池中
const addPending = (config: any) => {
const pendingKey = getPendingKey(config)
config.cancelToken =
config.cancelToken ||
new axios.CancelToken((cancel) => {//axios 取消请求(axios中取消请求情况分为1:取消该请求之后的相同请求;2:取消该请求之前的相同请求)
if (!pendingMap.has(pendingKey)) {
pendingMap.set(pendingKey, cancel)
const commonStore = useCommonStore()
commonStore.setCurRequestNum(pendingMap.size)//记录请求池中请求的数量
}
})
}
const removePending = (config: any) => {
const pendingKey = getPendingKey(config)
if (pendingMap.has(pendingKey)) {
const cancelToken = pendingMap.get(pendingKey)
cancelToken(pendingKey)//如果请求过则取消请求
pendingMap.delete(pendingKey)
setTimeout(() => {
const commonStore = useCommonStore()
commonStore.setCurRequestNum(pendingMap.size)
}, 100)
}
}
service.interceptors.request.use(
(config) => {
removePending(config)//移除重复的请求
addPending(config)//将新的请求添加进去
const token = getToken()
if (token) {
config.headers['X-Auth-Token'] = token
}
return config
},
(error) => {
return Promise.reject(error.response)
}
)
service.interceptors.response.use(
(response: AxiosResponse): AxiosResponse => {
removePending(response.config)
return response
},
(error) => {
return Promise.reject(error)
}
)
export default service