Vue中loading状态管理

为了提升用户体验,我们一般会在做耗时操作时显示loading效果,缓解用户等待的焦虑.通常情况下,我们的代码可能是这样子:

<div v-show="isLoading">
   loading...
</div>
new Vue({
  data:{
    isLoading:false
  },
  methods:{
    async fetchFn(){
        this.isLoading = true
        try{
          await getSomething()
        }catch(e){
          //
        }finally{
          this.isLoading = false
        }
    }
  }
})

这样,我们在调用fetchFn时,即可显示loading效果了。
这种实践不是不可以,只是每次都需要要用户手动的、命令式的来维护loading的状态,很容易出现漏写的情况,虽然通过事后的测试可能发现问题,但总归是麻烦的。
我们能否想到一种自我状态管理的情况,给每个异步的method都生成一个loading状态,然后在需要用到loading的地方,在视图里面订阅指定method的loading变化即可。
我们通过Vue插件来实现该功能:

var VueLoadingPlugin = {};
VueLoadingPlugin.install = (Vue,loadingNameSpace='tzLoading') => {
  Vue.mixin({
    data() {
      return {
        [loadingNameSpace]: {},
      };
    },
    beforeCreate() {
      const methods = this.$options.methods || {};
      Object.keys(methods).forEach(key => {
        let fn = methods[key];
        this.$options.methods[key] = function(...args) {
          this.$set(this[loadingNameSpace], key, true);
          let ret = fn.apply(this, args);
          if (ret && typeof ret.catch === "function") {
            return ret.finally(() => (this[loadingNameSpace][key] = false));
          } else {
            this[loadingNameSpace][key] = false;
            return ret;
          }
        };
      });
    },
  });
};

这样,当我们有如下代码时:

new Vue({
  methods:{
    async fetchFn(){
      await getSomething()
    }
  }
})

只需要在模版里面适当的地方订阅fetchFn的loading状态,然后,在每次fetchFn开始时都会自动出现加载效果,然后在fetchFn正常结束或出错后关闭加载效果:

<div v-show="tzLoading.fetchFn">
loading...
</div>