Vue网络请求 如何实现全局加载动画与提示框
2022-12-083.7k 阅读
一、Vue 网络请求基础回顾
在 Vue 项目开发中,网络请求是非常常见的操作。我们经常使用的库有 axios,它是一个基于 Promise 的 HTTP 客户端,可用于浏览器和 Node.js。在开始实现全局加载动画与提示框之前,我们先来简单回顾一下如何使用 axios 进行网络请求。
首先,通过 npm 安装 axios:
npm install axios
然后在 Vue 项目中引入并使用,一般我们会在 main.js
中进行全局配置:
import axios from 'axios'
import Vue from 'vue'
Vue.prototype.$http = axios
这样在 Vue 组件中就可以通过 this.$http
来发起网络请求了,例如:
export default {
methods: {
async fetchData() {
try {
const response = await this.$http.get('/api/data')
console.log(response.data)
} catch (error) {
console.error('请求失败', error)
}
}
}
}
以上代码展示了一个简单的 GET 请求,通过 await
来处理 Promise,使得异步操作看起来更像同步操作,增强了代码的可读性。
二、实现全局加载动画的思路
- 拦截器的作用
- 要实现全局加载动画,我们需要利用 axios 的拦截器。拦截器可以在请求或响应被 then 或 catch 处理前进行拦截。对于请求拦截器,我们可以在请求发出前显示加载动画;对于响应拦截器,我们可以在响应返回后隐藏加载动画。
- 以请求拦截器为例,它的基本结构如下:
axios.interceptors.request.use(config => {
// 在此处显示加载动画
return config
}, error => {
return Promise.reject(error)
})
- 在上述代码中,
config
是请求的配置对象,我们可以在返回config
前进行一些操作,比如显示加载动画。同样,响应拦截器的基本结构如下:
axios.interceptors.response.use(response => {
// 在此处隐藏加载动画
return response
}, error => {
// 在此处隐藏加载动画(如果请求失败)
return Promise.reject(error)
})
- 加载动画的实现方式
- 加载动画可以通过 CSS 动画或者使用一些 UI 库提供的加载组件来实现。例如,使用 CSS 动画实现一个简单的旋转加载图标:
<style>
.loading {
border: 4px solid rgba(0, 0, 0, 0.1);
width: 30px;
height: 30px;
border-radius: 50%;
border-top-color: #007BFF;
animation: spin 1s ease-in-out infinite;
-webkit-animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
</style>
<div class="loading" v-if="isLoading"></div>
- 在 Vue 组件中,我们可以通过一个数据属性
isLoading
来控制加载动画的显示与隐藏。例如:
export default {
data() {
return {
isLoading: false
}
}
}
- 结合 axios 拦截器,我们可以在拦截器中控制
isLoading
的值,从而实现全局加载动画的显示与隐藏。
三、在 Vue 中实现全局加载动画
- 创建加载动画组件
- 首先,我们创建一个
Loading.vue
组件,用于显示加载动画。
- 首先,我们创建一个
<template>
<div class="loading-mask" v-if="isLoading">
<div class="loading">
<div class="loading-spinner"></div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
isLoading: false
}
}
}
</script>
<style scoped>
.loading-mask {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z - index: 9999;
}
.loading {
width: 60px;
height: 60px;
position: relative;
}
.loading - spinner {
border: 4px solid rgba(0, 0, 0, 0.1);
width: 100%;
height: 100%;
border - radius: 50%;
border - top - color: #007BFF;
animation: spin 1s ease - in - out infinite;
-webkit - animation: spin 1s ease - in - out infinite;
}
@keyframes spin {
to {
-webkit - transform: rotate(360deg);
transform: rotate(360deg);
}
}
</style>
- 在 main.js 中配置拦截器与加载动画
- 在
main.js
中,我们引入Loading.vue
组件,并通过 Vue.mixin 来全局混入一个isLoading
属性和相关方法,同时配置 axios 拦截器。
- 在
import Vue from 'vue'
import axios from 'axios'
import Loading from './components/Loading.vue'
// 创建一个 Loading 实例
const loading = new Vue(Loading).$mount()
document.body.appendChild(loading.$el)
Vue.mixin({
data() {
return {
isLoading: false
}
},
methods: {
showLoading() {
this.isLoading = true
loading.isLoading = true
},
hideLoading() {
this.isLoading = false
loading.isLoading = false
}
}
})
axios.interceptors.request.use(config => {
this.$options.methods.showLoading.call(this)
return config
}, error => {
this.$options.methods.hideLoading.call(this)
return Promise.reject(error)
})
axios.interceptors.response.use(response => {
this.$options.methods.hideLoading.call(this)
return response
}, error => {
this.$options.methods.hideLoading.call(this)
return Promise.reject(error)
})
Vue.prototype.$http = axios
- 在上述代码中,我们首先创建了
Loading.vue
的实例,并将其挂载到document.body
上。然后通过 Vue.mixin 混入了isLoading
属性以及showLoading
和hideLoading
方法。在 axios 的请求和响应拦截器中,分别调用showLoading
和hideLoading
方法来控制加载动画的显示与隐藏。
四、实现全局提示框的思路
- 提示框的类型与用途
- 全局提示框通常用于向用户展示一些重要信息,比如请求成功或失败的提示。常见的提示框类型有成功提示、失败提示、警告提示等。
- 对于网络请求相关的提示,成功提示可以告知用户数据已成功获取或操作已成功完成;失败提示则用于展示请求失败的原因,帮助用户了解问题所在。
- 提示框的实现方式
- 与加载动画类似,我们可以通过创建一个提示框组件,并结合 Vue 的混入和事件总线来实现全局提示框。提示框组件可以通过接收不同的参数来显示不同类型的提示信息。
- 例如,我们可以定义一个
Toast.vue
提示框组件,它接收message
(提示信息)和type
(提示类型,如success
、error
、warning
)两个属性。
<template>
<div class="toast" :class="`toast - ${type}`" v - if="isVisible">
{{message}}
</div>
</template>
<script>
export default {
data() {
return {
isVisible: false,
message: '',
type:'success'
}
},
methods: {
show(message, type ='success') {
this.message = message
this.type = type
this.isVisible = true
setTimeout(() => {
this.isVisible = false
}, 3000)
}
}
}
</script>
<style scoped>
.toast {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background - color: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px 20px;
border - radius: 5px;
z - index: 9999;
opacity: 0;
transition: opacity 0.3s ease - in - out;
}
.toast - success {
background - color: #28a745;
}
.toast - error {
background - color: #dc3545;
}
.toast - warning {
background - color: #ffc107;
}
.toast.show {
opacity: 1;
}
</style>
- 在上述代码中,
show
方法用于显示提示框,通过setTimeout
在 3 秒后自动隐藏提示框。
五、在 Vue 中实现全局提示框
- 创建提示框组件
- 我们已经创建了
Toast.vue
组件,接下来在main.js
中配置它。
- 我们已经创建了
- 在 main.js 中配置提示框
- 在
main.js
中,我们引入Toast.vue
组件,并通过 Vue.mixin 混入显示提示框的方法。
- 在
import Vue from 'vue'
import axios from 'axios'
import Loading from './components/Loading.vue'
import Toast from './components/Toast.vue'
// 创建 Loading 实例
const loading = new Vue(Loading).$mount()
document.body.appendChild(loading.$el)
// 创建 Toast 实例
const toast = new Vue(Toast).$mount()
document.body.appendChild(toast.$el)
Vue.mixin({
data() {
return {
isLoading: false
}
},
methods: {
showLoading() {
this.isLoading = true
loading.isLoading = true
},
hideLoading() {
this.isLoading = false
loading.isLoading = false
},
showToast(message, type ='success') {
toast.show(message, type)
}
}
})
axios.interceptors.request.use(config => {
this.$options.methods.showLoading.call(this)
return config
}, error => {
this.$options.methods.hideLoading.call(this)
this.$options.methods.showToast.call(this, '请求失败', 'error')
return Promise.reject(error)
})
axios.interceptors.response.use(response => {
this.$options.methods.hideLoading.call(this)
this.$options.methods.showToast.call(this, '请求成功','success')
return response
}, error => {
this.$options.methods.hideLoading.call(this)
this.$options.methods.showToast.call(this, '请求失败', 'error')
return Promise.reject(error)
})
Vue.prototype.$http = axios
- 在上述代码中,我们创建了
Toast.vue
的实例并挂载到document.body
上。通过 Vue.mixin 混入了showToast
方法,在 axios 的响应拦截器中,根据请求的结果调用showToast
方法显示相应的提示信息。
六、优化与注意事项
- 多次请求处理
- 当有多个网络请求同时进行时,加载动画的显示与隐藏需要进行合理处理。例如,我们可以通过一个计数器来记录当前正在进行的请求数量。在请求拦截器中计数器加 1,在响应拦截器中计数器减 1,只有当计数器为 0 时才隐藏加载动画。
- 以下是修改后的代码示例:
import Vue from 'vue'
import axios from 'axios'
import Loading from './components/Loading.vue'
import Toast from './components/Toast.vue'
// 创建 Loading 实例
const loading = new Vue(Loading).$mount()
document.body.appendChild(loading.$el)
// 创建 Toast 实例
const toast = new Vue(Toast).$mount()
document.body.appendChild(toast.$el)
Vue.mixin({
data() {
return {
requestCount: 0
}
},
methods: {
showLoading() {
if (this.requestCount === 0) {
loading.isLoading = true
}
this.requestCount++
},
hideLoading() {
this.requestCount--
if (this.requestCount === 0) {
loading.isLoading = false
}
},
showToast(message, type ='success') {
toast.show(message, type)
}
}
})
axios.interceptors.request.use(config => {
this.$options.methods.showLoading.call(this)
return config
}, error => {
this.$options.methods.hideLoading.call(this)
this.$options.methods.showToast.call(this, '请求失败', 'error')
return Promise.reject(error)
})
axios.interceptors.response.use(response => {
this.$options.methods.hideLoading.call(this)
this.$options.methods.showToast.call(this, '请求成功','success')
return response
}, error => {
this.$options.methods.hideLoading.call(this)
this.$options.methods.showToast.call(this, '请求失败', 'error')
return Promise.reject(error)
})
Vue.prototype.$http = axios
- 提示框文案优化
- 对于提示框的文案,应该根据具体的业务场景进行优化。例如,在请求失败时,根据不同的错误状态码显示不同的提示信息。如果是 404 错误,可以提示“资源未找到”;如果是 500 错误,可以提示“服务器内部错误,请稍后重试”等。
- 以下是修改后的响应拦截器代码示例:
axios.interceptors.response.use(response => {
this.$options.methods.hideLoading.call(this)
this.$options.methods.showToast.call(this, '请求成功','success')
return response
}, error => {
this.$options.methods.hideLoading.call(this)
let errorMessage = '请求失败'
if (error.response) {
switch (error.response.status) {
case 404:
errorMessage = '资源未找到'
break;
case 500:
errorMessage = '服务器内部错误,请稍后重试'
break;
default:
errorMessage = `请求失败,状态码: ${error.response.status}`
}
}
this.$options.methods.showToast.call(this, errorMessage, 'error')
return Promise.reject(error)
})
- 样式与兼容性
- 在实现加载动画和提示框时,要注意样式的兼容性。对于 CSS 动画,不同的浏览器可能有不同的表现,需要添加相应的浏览器前缀。例如,
-webkit - transform
用于 WebKit 内核的浏览器(如 Safari)。 - 同时,提示框和加载动画的样式应该与整个项目的风格保持一致,以提供良好的用户体验。
- 在实现加载动画和提示框时,要注意样式的兼容性。对于 CSS 动画,不同的浏览器可能有不同的表现,需要添加相应的浏览器前缀。例如,
通过以上步骤,我们在 Vue 项目中实现了全局加载动画与提示框,使得网络请求过程更加友好和可视化,提高了用户体验。在实际项目中,可以根据具体需求对代码进行进一步的优化和扩展。