原因

先看一段简单、常规的vue代码。

<template>
  <div v-if="list && list.length>0">
    <div v-for="row in list">
      <span>{
  {row.xxx}}</span>
      //显示剩下数据
    </div>
  </div>
  <div v-else>
    <div>暂无数据</div>
  </div>
</template>
<script>
export default {
  created() {
    this.ajaxform('/chronic/xxx').then(res => {
      this.list = res.body
    })
  }
}
</script>

这段代码在created生命周期函数内会调用后端接口获取数据,我们假设获取数据的时间需要1秒。那么打开页面后将显示“暂无数据”,1秒后才会显示列表数据,给客户的体验有些不好。这篇文章就来解决这个打开页面先显示暂无数据的问题。

思路

我们希望能加入一个过渡状态,在ajax请求加载数据时显示“数据加载中”,数据加载完成后再根据有无数据来显示列表或是“暂无数据”。当跳转页面时,流程如下图:

但不能所有的ajax请求都显示数据加载中,只能是打开页面后立即执行的ajax请求显示加载中,也就是created、mounted中执行的ajax请求显示加载中。

方案

首先我们需要一个全局变量来储存当前正在执行的ajax请求的数量以及是否是页面刚打开的状态。我将这两个变量存在了store中

const state = {
  pageInit: false,
    // 记录有几个ajax请求正在执行中
  ajaxCount: 0,
}
const mutations = {
  updatePageInit: function (state, pageInit) {
    state.pageInit = pageInit
  },
  updateAjaxCount: function (state, count) {
    state.ajaxCount = state.ajaxCount + count
  }
}
export default {
  namespaced: true,
  state,
  mutations
}

然后因为项目中发起ajax请求的组建使用的是axios,所以可以在axios的request、response拦截器中,对这个数进行加减。请求开始,则对ajaxCount加1,请求成功或是失败对ajaxCount减1

axios.interceptors.request.use(
  config => {
    store.commit('user/updateAjaxCount', 1)
    return config
  },
  error => {
    store.commit('user/updateAjaxCount', -1)
    return Promise.reject(error)
  }
)
axios.interceptors.response.use(
  response => {
    store.commit('user/updateAjaxCount', -1)
    return response
  },
  error => {
    store.commit('user/updateAjaxCount', -1)
    return Promise.reject(error)
  }
)

在router的afterEach函数中对pageInit的值进行更新,也就是设置为刚打开页面的状态

router.afterEach(function (to, from) {
  store.commit('user/updatePageInit', true)
})

再然后在App.vue中要做两件事情

1、在template中,通过私有变量loading的值显示、隐藏<router-view>和加载状态(菊花动图)。

2、对ajaxCount进行watch,当ajaxCount大于0,且是刚进入页面的状态时,设置私有变量loading为true,显示加载中状态。

     当ajaxCount等于0时,也就是ajax请求全部结束时,设置私有变量loading为false,关闭加载中状态,设置pageInit为false,显示数据。

<template>
  <div style='height:100%;'>
    <router-view v-show="!loading"></router-view>
    <div v-show="loading"><img src="xxx/juhua.gif"></div><!-- 转菊花的动图 -->
  </div>
</template>
<script>
export default {
  name: 'app',
  data () {
    return {
      loading: false
    }
  },
  computed: mapState({
    pageInit: state => state.user.pageInit,
    ajaxCount: state => state.user.ajaxCount
  }),
  watch: {
    ajaxCount (count) {
      // count>0表示有ajax请求正在执行中, pageInit表示刚进入新页面, loading表示菊花转的状态
      if (count > 0 && this.pageInit && this.loading === false) {
        this.loading = true
      }
      if (count === 0) {
        this.loading = false
        this.$store.commit('user/updatePageInit', false)
      }
    }
  }
}
</script>

以下是效果图,第一张加载中状态是转菊花的图,第二张加载中状态是骨架屏的图

       

结语

我们加载中状态显示的很简单,只是转菊花,这里也可以做的效果更好一些,例如骨架屏等。