文章目录
- 目标
- 过程与代码
- loading效果
- mainStore控制loading显示
- 点击蒙板取消网络请求
- 在网络请求处添加对loading的控制
- 效果
- 总代码
- 修改或添加的文件
- loading.vue
- service/request/index
- store/modules/main
- App.vue
- 参考
本项目博客总结:【前端】Vue项目:旅游App-博客总结
目标
在发送网络请求的时候显示loading:
过程与代码
loading效果
本App中任何一个地方发送网络请求都要显示loading效果,因此我们要把它封装在一个组件中。且在App.vue中引入。
css代码解释,此做法可以:
- 让明确宽高的盒子垂直水平居中
- 让无宽高的盒子填满父容器
- 这里是第二种
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
loading总代码:
<template>
<div class="loading">
<div class="bg">
<img src="@/assets/img/home/full-screen-loading.gif" alt="">
</div>
</div>
</template>
<script setup>
</script>
<style lang="less" scoped>
.loading {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
// 蒙板颜色
background-color: rgba(0, 0, 0, .2);
// loading的效果会在任何效果之上
z-index: 999;
// 垂直水平居中
display: flex;
justify-content: center;
align-items: center;
.bg {
background: url(@/assets/img/home/loading-bg.png) 0 0 /100% 100%;
width: 104px;
height: 104px;
display: flex;
justify-content: center;
align-items: center;
img {
width: 70px;
height: 70px;
margin-bottom: 9px;
}
}
}
</style>
效果:背景有点灰色的地方就是蒙板
mainStore控制loading显示
我们的需求:在发送网络请求(简单来说就是加载数据)的时候显示loading。这样更加用户友好。
显然,App中很多页面都有可能要加载数据,因此我们需要把loading显示的控制放在一个地方统一管理。不难想到要放到store的mainStore中管理(上篇管理整个App时间设置的地方)。
useMainStore:
isLoading:false
html:
<div class="loading" v-if="mainStore.isLoading">
js:
import useMainStore from '../../store/modules/main';
const mainStore=useMainStore()
点击蒙板取消网络请求
这是一个常见的效果。
html:
<div class="loading" v-if="mainStore.isLoading" @click="loadingClick()">
js:
function loadingClick() {
mainStore.isLoading = false
}
在网络请求处添加对loading的控制
我们的需求:在发送网络请求的时候显示loading。
而我们在代码中封装了网络请求的功能,很显然,我们可以在request中添加在对loading的控制,如:
request(config) {
loadingStore.changeLoading(true);
mainStore.isLoading = true
return new Promise((resolve, reject) => {
this.instance
.request(config)
.then((res) => {
resolve(res.data);
mainStore.isLoading = false
})
.catch((err) => {
console.log("request err:", err);
reject(err);
mainStore.isLoading = false
})
.finally(() => {
loadingStore.changeLoading(false);
});
});
在发送请求时显示loading,发送完或发送完都取消显示。
但是不推荐这样做,我们希望一个方法只做一个功能。这里推荐使用拦截器。
拦截器 | Axios 中文文档 | Axios 中文网 (axios-http.cn)
axios 请求拦截器&响应拦截器 - 简书 (jianshu.com)
注意,拦截器是绑定在实例上的。我们要把它写在构造函数里。
constructor(baseURL) {
this.instance = axios.create({
baseURL,
timeout: TIMEOUT,
});
this.instance.interceptors.request.use(config => {
// 发送网络请求前要做的事
mainStore.isLoading = true
return config
}, err => {
// 发送网络请求失败要做的事
return err
})
this.instance.interceptors.response.use(res => {
// 响应成功要做的事
mainStore.isLoading = false
return res
}, err => {
// 响应失败要做的事
mainStore.isLoading = false
return err
})
}
效果
发送网络请求时会显示loading,数据加载完后就消失。
总代码
修改或添加的文件
loading.vue
loading组件。
<template>
<div class="loading" v-if="mainStore.isLoading" @click="loadingClick()">
<div class="bg">
<img src="@/assets/img/home/full-screen-loading.gif" alt="">
</div>
</div>
</template>
<script setup>
import useMainStore from '../../store/modules/main';
const mainStore = useMainStore()
function loadingClick() {
mainStore.isLoading = false
}
</script>
<style lang="less" scoped>
.loading {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
// 蒙板颜色
background-color: rgba(0, 0, 0, .2);
// loading的效果会在任何效果之上
z-index: 999;
// 垂直水平居中
display: flex;
justify-content: center;
align-items: center;
.bg {
background: url(@/assets/img/home/loading-bg.png) 0 0 /100% 100%;
width: 104px;
height: 104px;
display: flex;
justify-content: center;
align-items: center;
img {
width: 70px;
height: 70px;
margin-bottom: 9px;
}
}
}
</style>
service/request/index
在网络请求的代码中添加请求拦截器和响应拦截器。
import axios from "axios";
import { useLoadingStore } from "@/store/modules/loading";
import useMainStore from "../../store/modules/main";
import { baseURL, TIMEOUT } from "./config";
const loadingStore = useLoadingStore();
const mainStore = useMainStore()
class HYRequest {
constructor(baseURL) {
this.instance = axios.create({
baseURL,
timeout: TIMEOUT,
});
this.instance.interceptors.request.use(config => {
// 发送网络请求前要做的事
mainStore.isLoading = true
return config
}, err => {
// 发送网络请求失败要做的事
return err
})
this.instance.interceptors.response.use(res => {
// 响应成功要做的事
mainStore.isLoading = false
return res
}, err => {
// 响应失败要做的事
mainStore.isLoading = false
return err
})
}
request(config) {
loadingStore.changeLoading(true);
return new Promise((resolve, reject) => {
this.instance
.request(config)
.then((res) => {
resolve(res.data);
})
.catch((err) => {
console.log("request err:", err);
reject(err);
})
.finally(() => {
loadingStore.changeLoading(false);
});
});
}
get(config) {
return this.request({ ...config, method: "get" });
}
post(config) {
return this.request({ ...config, method: "post" });
}
}
export default new HYRequest(baseURL);
store/modules/main
添加isLoading来控制loading的显示。
import { defineStore } from "pinia"
const startDay = new Date()
const endDay = new Date()
endDay.setDate(startDay.getDate() + 1)
const useMainStore = defineStore('main', {
state: () => ({
token:'',
startDay:startDay,
endDay:endDay,
isLoading:false,
}),
actions: {
}
})
export default useMainStore
App.vue
添加loading组件。
<template>
<div class="app">
<router-view/>
<tab-bar v-if="!$route.meta.hideTabBar"></tab-bar>
<loading/>
</div>
</template>
<script setup>
import tabBar from './components/tab-bar/tab-bar.vue';
import loading from './components/loading/loading.vue';
import { useRoute } from 'vue-router'
const route=useRoute()
</script>
<style lang="less" scoped>
</style>
参考
css中 top, right, bottom, left设置为0有什么用?它和width:100%和height:100%有什么区别?
CSS Backgrounds(背景) | 菜鸟教程 (runoob.com)
拦截器 | Axios 中文文档 | Axios 中文网 (axios-http.cn)
axios 请求拦截器&响应拦截器 - 简书 (jianshu.com)