手摸手,配置项目中全局loading

3,643 阅读3分钟

在后台管理项目中接口的请求响应速度取决于很多因素,如网络、带宽、同时发起的请求数量===,无法将请求结果及时响应的页面上,而我们又需要告诉用户,数据已经在加载了,你需要先等一等,喝杯茶。这个时候就需要 loading了 ,但是一个项目里又不仅仅是只有一个请求,每个都写一遍 loading ? 可能真的有人这样干,但是我懒啊!我就想写一个 全局loading 需要他的时候他出来,不需要的时候他离开🤣,他来了----

全局loading

涉及知识 vue(一点点---)、vuex、axios拦截器、vue-router拦截器

新建一个 loading.js 文件( ps:只要你找的到放到哪里都行!瞎说的🐛)

  // 引入 store 也就是 vuex
  import store from '../store/index'
  // 声明一个用于计数的变量
  let loadingNum = 0

  const startLoading = () =>{
    // 在首页发起的请求不进行累加 (这个判断是因为项目内又一个首页大屏不需要这个全局 loading,无的话可以直接去除!)
    if (global.location.hash !== '#/home') {
      loadingNum ++
      store.commit('setChangeLoading', true)
    }
  }

  const endLoading = () =>{
  	// 此处同上
    if (global.location.hash !== '#/home') {
      loadingNum --
      
      // 这个纯属是防止某些极端情况下变量减成负数(当然此处可以优化  但是我觉得这样写意图更明显!)
      loadingNum = loadingNum < 0 ? 0 : loadingNum
      if (!loadingNum) {
        store.commit('setChangeLoading', false)
      }
    }
  }

  // 重置函数
  const resetLoading = (store) =>{
    // 每次路由变化 重置loading
    loadingNum = 0
    store.commit('setChangeLoading', false)
  }

  // 导出函数
  export default {
    startLoading,
    endLoading,
    resetLoading
  }

vuex 配置

既然是全局的肯定带有一个地方存状态不是,项目里有vuex不用白不用啊!什么 没有怎么办 localStorage,sessionStorage 也是一样的无非是存个全局变量。

// state
state: {
  loadingState: false
},
// 在 mutations 中写一个与上边同名的 setChangeLoading 函数
mutations: {
  setChangeLoading(state, data) {
    state.loadingState = data
  }
}

vue-router 路由拦截器配置

这里主要是为了页面切换重置 全局loading 的状态, 路由在变化以后,用户其实是不在关心数据有没有响应完毕,假如用户在上一个路由加载的数据比较多,数据响应时间比较长,在用户切换路由时数据并未加载完毕,又发起了新的请求。那么全局loading其实是会受到历史路由的影响的。

// 记得引入 loading.js
import loading from '../utils/loading'

// 创建一个路由 全局前置守卫
router.beforeEach((to, from, next) => {
  // 记得引入 loading.js
  // 也不关心他三个参数是干啥的 反正路由变了 我就重置 全局loading
  loading.resetLoading()
})

axios 拦截器配置

这个其实也没啥说的,如果理解拦截器就应该知道要做啥了,1. 发起请求加载 全局loading 2. 请求加载完毕及发生错误关闭全局loading

// 这个文件是你封装的 axios 文件内


// 1. 引入loading、store  不会有人问为啥要引入吧?
import loading from '../utils/loading'
import store from '../store'


// 请求拦截器
service.interceptors.request.use(async (config) => {
  // 开始请求加载
  loading.startLoading()
  
  return config
}, err => {
  // 发生错误清除
  loading.endLoading()
  return Promise.reject(err)
})


// 响应拦截器
service.interceptors.response.use(response => {
  // 加载完毕清除 
  loading.endLoading()
}, err => {
  // 发生错误清除 
  loading.endLoading()
  return Promise.reject(handleError(err));
});

那我要是想某些请求不加载全局loading呢? 咱都有拦截器了这不是很简单?

// 在此数组中不加载全局loading 根据 url匹配
const loadingWhitelist = [
  '/place/xxx/12121'
]
// 判断是否包含指定url
function isLoadingWhite (url) {
  //indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。如果没有找到匹配的字符串则返回 -1。
  return loadingWhitelist.some(item => { return url.indexOf(item) !== -1})
}

// 请求拦截器
service.interceptors.request.use(async (config) => {

  // 判断加载 全局loading (config里包含了配置信息及请求信息, 具体可以打印看一下)
  if (!isLoadingWhite(config.url)) { loading.startLoading() }

  return config
}, err => {
  loading.endLoading()
  return Promise.reject(err)
})

在app.vue内使用

<template>
  <div id="app">
    <router-view/>
    
    <el-col class="loading"
            v-show="$store.state.loadingState"
            element-loading-text="正在加载中..."
            element-loading-spinner="el-icon-loading"
            element-loading-background="rgba(0, 0, 0, 0.5)"
            v-loading="$store.state.loadingState">
    </el-col>
  </div>
</template>
<script>
  export default {
    name: 'App'
  }
</script>
<style lang="stylus">
  #app{
  .loading {
      position absolute
      top: 0
      left 0
      width 100%
      height 100vh
      z-index 999999
      background rgba(94, 171, 198, 0.1)
    }
  }
</style>

其实我是放在layout主体布局文件内使用的!

结尾

任何东西都没有百分之百适用于所有人,写这个更多是为了写 全局loading 的实现思路,所以照搬可能会有问题,如要使用还是要结合自己的理解。谢谢!