前言
在项目中使用loading
,一般是在js中改一个变量,或者调用一个service
中的方法,比如Element Ui
中就提供了这两种方式的loading
,这样做有很好的扩展性。
BUT
,如果你做一个后台管理项目,有一个api
就要这样重复一下代码,emmm...
我是接受不了,看我这个懒人是如何在vue
项目中,把自动全局的loading
封装到axios
中的吧。
还有,就是我看很多朋友还在写这样的代码:
/**
* 本文简单设想,后端返回的是比较标准的code、data、message
*/
api.http('url' , data).then(res => {
if (res.code === 200) {
// 执行成功的操作
} else {
// 错误提示,当然这里只是举例,并非说真的alert,但是你每个请求都要写,多累啊!
alert(res.message)
}
})
复制代码
准备工作
- 一个相对标准的
vue
项目,可以选择用vue-cli
脚手架构建; - 引入
axios
,理论上直接npm install axios
; - 建议在项目的
src
目录下创建一个utils
目录,可以分文件去写一些filter
、公共方法等...; - 在
utils
目录下创建http.js
(想怎么命名都可以,有意思就行,你们随意~)
OK,接下来就要在http.js
中封装一下axios
.
封装 axios
这里话不多说,直接上代码了
import axios from 'axios'
import store from '../store' // 这里做loading会调用store
import { getToken } from '@/utils/auth' // 这个不重要,就是一个获取tooken的
// 创建axios实例
const service = axios.create({
// api的base_url,本地配置的代理,理论上可以不用baseURL
// baseURL: process.env.BASE_API,
timeout: 15000, // 请求超时时间
// headers 可以通过在这里设置,也可以在request拦截器里创建
// headers: {'X-Custom-Header': 'foobar'}
})
// request拦截器
service.interceptors.request.use(config => {
// !!! 这里开始触发 loading 效果 !!!
store.dispatch('SetLoading', true)
// 设置 token header
getToken() && (config.headers['token'] = token)
// 这个是微信登录中需要用到 header
config.headers['deviceType'] = 'school_admin_web'
return config
},
error => {
const { response } = error
// 这里可以根据自己的业务做一些操作,比如说全局提示
Promise.reject(error)
})
// respone拦截器
service.interceptors.response.use(
response => {
// !!! 关闭 loading !!!
store.dispatch('SetLoading', false)
const res = response.data
if (res.code !== 200) {
// 这里可以做一些全局性的错误提示,这样就没必要在每个请求都再写一个else,再重复代码提示
alert(res.message)
// 不是真的alert啊
}
return response.data
},
error => {
// !!! 关闭 loading !!!
store.dispatch('SetLoading', false)
const { response } = error
// 这里可以根据自己的业务做一些操作,比如说强制登出
return Promise.reject(error)
}
)
export default service
复制代码
好了,可以看到,其实全局的错误提示,在上面的代码中有直接的体现了,这里就不多讲了,那全局的loading
的话,这里其实调用了store
里的SetLoading
。
如果您还没用过store
,请自行学习文档,这里不做store
的详细讲解。
使用 store 控制 loading 状态
这里,假想您已经熟悉store并使用store的模块化(当然,不模块化也无所谓,看你自己)。 假设store
目录下有个 app module
对应的是app.js
,好了,上代码:
const app = {
state: {
requestLoading: 0,
},
mutations: {
SET_LOADING: (state, boolean) => {
boolean ? ++state.requestLoading : --state.requestLoading
},
},
actions: {
SetLoading ({ commit }, boolean) {
commit('SET_LOADING', boolean)
},
},
}
export default app
复制代码
结合对axios
的封装代码可以看到,在axios
中request
拦截器中,我调用了store SetLoading
,改变了requestLoading
的数值,使其自加1;在response
拦截器中,我同样调用了store SetLoading
,改变requestLoading
的数值,使其自减1。其整体原理,类似于垃圾回收机制,这样做的好处是,有多个请求并发时,只有所有的请求都返回结果后,loading
效果才会消失。
那么,现在焦点只处于store
下app
模块的requestLoading
状态,我们拿这个判断loading
效果即可。
那么,剩余的代码,这里就不多说了,
大抵是你在你的布局层里,定位一个阴影层的loading,通过requestLoading的状态,来判断显示隐藏(类似于这样)。
<template>
<section class="app-main">
<div class="request-loading" :class="{'request-loading-show' : requestLoading}">
<div class="loading-module"></div>
</div>
<transition name="fade-transform" mode="out-in">
<router-view/>
</transition>
</section>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'AppMain',
computed: {
...mapGetters([
'requestLoading',
]),
},
}
</script>
// .request-loading-show样式代码这里就不写了,自己根据需要弄一下吧。
复制代码
最终使用
这里也不用多说了,就是把第二部封装好的axios import
使用就OK,这里建议在src
下创建api
目录,来模块定义所有的api
,比如:
import http from '@/utils/http'
export default {
add (data) {
return http({ url: '...', data: data, method: 'post' })
},
}
// 然后在组件里正常调用即可...
复制代码
总结
通过以上的操作,有两个好处,请求期间会自动显示loading效果;当有错误时,全局已经做好了提示。