# 实现请求可配置是否展示全局加载动画
很多接口请求都需要展示全局加载动画,每次请求就写一行代码开启动画,请求结束和响应结束就关闭动画,这显得很繁琐,且存在一个问题:那就是当多个请求时,可能会导致其余接口还没请求结束,但是加载动画被前面某个请求结束的接口给提前关闭了
解决方法: 每当请求接口时,累加请求接口数量,响应时累减请求接口数量,当请求数量为0 时 才关闭全局请求动画。
上代码
# 第一步:新增全局加载动画组件
<template>
<div :class="['xiaobu-loading-mask', show ? 'show' : 'hide']" v-show="show">
<div class="loading-wrapper">
<span class="loading-dot loading-dot-spin">
<i></i>
<i></i>
<i></i>
<i></i>
</span>
</div>
</div>
</template>
<script>
export default {
name: "Loading",
unAutoRegister: true,
computed: {
show() {
return this.$store.state.app.showLoading
}
}
}
</script>
<style lang="scss" scoped>
.xiaobu-loading-mask {
position: fixed;
left: 0;
top: 0;
height: 100%;
width: 100%;
background: rgba(255, 255, 255, 0.3);
user-select: none;
z-index: 9999;
overflow: hidden
}
.loading-wrapper {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -100%)
}
.loading-dot {
animation: antRotate 1.2s infinite linear;
transform: rotate(45deg);
position: relative;
display: inline-block;
font-size: 64px;
width: 37px;
height: 37px;
box-sizing: border-box
}
.loading-dot i {
width: 15px;
height: 15px;
position: absolute;
display: block;
@include background_color("main_color");
border-radius: 100%;
transform: scale(.75);
transform-origin: 50% 50%;
opacity: .3;
animation: antSpinMove 1s infinite linear alternate
}
.loading-dot i:nth-child(1) {
top: 0;
left: 0
}
.loading-dot i:nth-child(2) {
top: 0;
right: 0;
-webkit-animation-delay: .4s;
animation-delay: .4s
}
.loading-dot i:nth-child(3) {
right: 0;
bottom: 0;
-webkit-animation-delay: .8s;
animation-delay: .8s
}
.loading-dot i:nth-child(4) {
bottom: 0;
left: 0;
-webkit-animation-delay: 1.2s;
animation-delay: 1.2s
}
@keyframes antRotate {
to {
-webkit-transform: rotate(405deg);
transform: rotate(405deg)
}
}
@-webkit-keyframes antRotate {
to {
-webkit-transform: rotate(405deg);
transform: rotate(405deg)
}
}
@keyframes antSpinMove {
to {
opacity: 1
}
}
@-webkit-keyframes antSpinMove {
to {
opacity: 1
}
}
</style>
# 第二步:在app.vue文件上引入动画组件
<template>
<div id="app">
<!-- 加载动画,要在router-view上面,解决双加载动画问题 -->
<loading></loading>
<!-- 页面切换容器 -->
<router-view class="xiaobu-app-container"/>
</div>
</template>
<script>
import Loading from "@/components/loading"
export default {
name: "App",
components: {
Loading
}
}
</script>
<style>
#app {
}
</style>
# 第三步:vuex新增showLoading变量,以及设置showLoading值的方法
const state = {
showLoading: false //展示全局加载动画
}
const mutations = {
//设置请求动画状态
SET_LOADING_STATUS: (state, showLoading) => {
state.showLoading = showLoading
},
}
const actions = {
}
export default {
namespaced: true,
state,
mutations,
actions
}
# 第四步:实现累加,以及累减方法
// req-loading.js 文件
import store from "@/store"
let count = 0 // 监听计数中间变量
/**
*
* @param {Number} count http请求列队个数
*/
function showLoading(count) {
// 当http请求列队个数为0时关闭loading
if (count === 0) {
store.commit("app/SET_LOADING_STATUS", false)
} else {
store.commit("app/SET_LOADING_STATUS", true)
}
}
export function addHttpLoading() {
count++
showLoading(count)
}
// http请求列队计数减一
export function removeHttpLoading() {
count--
showLoading(count)
}
# 第五步:在axios请求文件上使用
import axios from "axios"
import Message from "@/plugins/reset-message.js"
import {addHttpLoading, removeHttpLoading} from "./req-loading"
import {getToken} from "@/utils/cookie"
import store from "@/store"
// 创建axios实例
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_URL,
timeout: process.env.VUE_APP_TIME_OUT // 请求超时时间
})
// request拦截器
service.interceptors.request.use(
config => {
addHttpLoading()
const token = getToken()
if (token) {
config.headers.Authorization = "Bearer " + token
}
return config
},
error => {
Promise.reject(error)
}
)
// respone拦截器
service.interceptors.response.use(
async response => {
removeHttpLoading()
const res = response.data
// 如果自定义代码不是200,则判断为错误。
if (response.status !== 200) {
Message({
message: res.message || "Error",
type: "error",
duration: 5 * 1000
})
return Promise.reject(new Error(res.message || "Error"))
} else {
// code为200 或 返回的是文件流 直接返回
if (res.code === 200 || response.config.responseType === "blob") {
return res
}
Message({
message: res.message || "Error",
type: "error",
duration: 5 * 1000
})
if (res.code === 401) {
await store.dispatch("user/logout")
window.location.reload()
}
return Promise.reject(new Error(res.message || "Error"))
}
},
error => {
removeHttpLoading()
if (error.name === "AxiosError") {
Message.error(error.message)
}
return Promise.reject(new Error(error))
}
)
export default service
这样即可实现有请求就会带上动画,所有请求结束才关闭动画
# 第六步:实现可配置是否要展示加载动画
在axios请求中的config添加变量
/**
* @description: 登录
* @param {*}
* @return {*}
* @author: syx
*/
export function login(data = {}, config = {
showLoading: true
}) {
return axiosRequest.post("/xiaobu-admin/login", data, config)
}
在第五步的文件中,新增判断
// 请求拦截中判断
service.interceptors.request.use(
config => {
if (config.showLoading) {
addHttpLoading()
}
return config
},
error => {
Promise.reject(error)
}
)
// 响应拦截中判断
service.interceptors.response.use(
async response => {
if (response.config.showLoading) {
removeHttpLoading()
}
const res = response.data
return res
},
error => {
// 有配置showLoading为true的接口,请求异常才会减少请求接口
if (error.config&&error.config.showLoading) {
removeHttpLoading()
}
if (error.name === "AxiosError") {
Message.error(error.message)
}
return Promise.reject(new Error(error))
}
)
# 第七步:以上6步即可实现可配置的展示加载动画,但是存在一个问题,就是可能会出现双加载动画的问题。
解决方法: 采用css解决此问题,思路就是 出现全局加载动画的时候 里面的动画添加display:none样式。
以element的loading为例。其加载动画类名为 el-loading-mask。
全局添加CSS代码
.xiaobu-loading-mask.show + .xiaobu-app-container .el-loading-mask{
display: none;
}
这就是为什么 loading组件要放在router-view前面的原因
WARNING
element的loading动画不能放在body元素下。放在body的话只能通过js判断了,能用css解决的问题就不要用js,所以为了避免出现问题,请不要将loading的body设置为true。
# 总结
此篇文章为axios请求优化其一,关注微信公众号【爆米花小布】不迷路。如有问题,可私信。