阅读 158

【前端实践系列之十】教你封装伴随请求的全局Loading!

这是我参与更文挑战的第11天,活动详情查看: 更文挑战 !

👽 概论

Loading是每个项目中必不可少的组件之一,它除了可以提升用户等待体验外,也可以起到防止误操作、事件防抖的作用,其形态也有很多种:组件内loading、按钮loading以及全局loading。

全局loading往往应该伴随着异步请求出现:请求开始时loaidng展示,请求结束时loading消失。知道这一基本需求外,我们就来一起封装一个基于请求的全局loading。

👽 组件准备

首先准备好我们的loading组件,这个组件与大家常用的自定义组件并无二致。此阶段有两个要点:

1. 组件cursor使用not-allowed,以防事件穿透;
2. 组件样式的层级一定要够高,以免无法遮住所有元素;
复制代码
<!--index.vue-->
<template>
  <div class="global-box">
    <div class="wrap">
      <div class="loading">
        <div class="bounceball"></div>
        <div class="text">LOADING</div>
      </div>
    </div>
  </div>
</template>
<script>
export default {};
</script>

<style lang="less" scoped>
@import './index.less';
</style>
复制代码
//index.less
@width: 15px;
@height: 15px;

@bounce_height: 30px;

.global-box {
  overflow: hidden;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 9999;
  width: 100vw;
  height: 100vh;
  background-color: rgba(0, 0, 0, 0.3);
  cursor: not-allowed;
}

.wrap {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.text {
  display: inline-block;
  margin-left: 5px;
  color: #ff4600;
}

.bounceball {
  display: inline-block;
  position: relative;
  margin-right: 10px;
  width: @width;
  height: 37px;

  &::before {
    display: block;
    position: absolute;
    top: 0;
    border-radius: 50%;
    width: @width;
    height: @height;
    background-color: #ff4600;
    transform-origin: 50%;
    animation: bounce 500ms alternate infinite ease;
    content: '';
  }
}

@keyframes bounce {
  0% {
    top: @bounce_height;
    border-radius: 60px 60px 20px 20px;
    height: 5px;
    transform: scaleX(2);
  }

  35% {
    border-radius: 50%;
    height: @height;
    transform: scaleX(1);
  }

  100% {
    top: 0;
  }
}
复制代码

可以将其作为组件直接挂载在页面上,看看现在的效果:

1.gif

👽 挂载插件

挂载插件是该次封装流程的关键,这一过程中的总体思想是:

打开Loading,将Loading插件挂载到页面上;

关闭Loading,也就是将Loading插件从页面上卸载。
复制代码

道理很简单,直接上实践。

//mountLoading.js
import Vue from 'vue';
import Loading from './index';

const LoadingConstructor = Vue.extend(Loading);//定义Loading的构造器

const LoadingInstance = new LoadingConstructor({
  el: document.createElement('div'),
});//创建实例

const loading = {
  show() {
    // 显示方法:挂载实例到body上
    document.body.appendChild(LoadingInstance.$el);
  },
  hide() {
    // 隐藏方法:从body上写在实例
    document.body.removeChild(LoadingInstance.$el);
  },
};

//创建Loading插件
export default {
   //插件是一个对象时,必须提供 install 方法
  install() {
    //绑定$loading
    !Vue.$loading && (Vue.$loading = loading);
    //全局混入
    Vue.mixin({
      created() {
        this.$loading = Vue.$loading;
      },
    });
  },
};
复制代码
//main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router';

import Loading from './loading/mountLoading';

Vue.use(Loading); // 使用loading插件

Vue.config.productionTip = false;
new Vue({
  router,
  render: (h) => h(App),
}).$mount('#app');

复制代码

此时loading插件已挂载成功,在页面中使用this.$loading.show()this.$loading.hide()可成功改变loading状态。

👽 请求跟随

这一步主要是利用了axios的拦截器,总体思想:

请求拦截器中打开loading;
响应拦截器中关闭loading。
复制代码
//request.js

import axios from 'axios';
import Vue from 'vue';

const instance = axios.create({
  baseURL,
  timeout: 30000,
});


// 请求拦截器
instance.interceptors.request.use((config) => {
  Vue.$loading.show();

  return config;
});

// 响应拦截器
instance.interceptors.response.use(
  (res) => {
    Vue.$loading.hide();
    return res;
  },
  (err) => {
    console.log('err: ', err);
    Vue.$loading.hide();
  },
);

export default instance;
复制代码

👽 结语

铁汁们,你学会了吗👻?

文章分类
前端
文章标签