Add Global Singleton Loading for Http Requests
我有一个美丽的愿望:Don't repeat yourself.
#
需求背景#
为什么要添加全局 Loading虽然现有组件库提供了 Loading 的封装方法,可以通过以下的代码,在请求开始前开启 Loading,请求结束后关闭Loading。但是如果对很多个请求都要写类似这样的代码,还是略麻烦。所以现在的需求是为 Http 请求添加全局 Loading。
#
如何在不影响项目已有业务代码的前提下添加在我们的项目已有业务代码里,并不是所有的请求都添加了 Loading 效果,只是针对部分耗时较长的请求。
为了不影响已有代码,为全局的 Http 请求方法添加可配置参数 showLoader 来控制是否添加 Loading,默认为false。
#
如何处理串行、并行的多个请求串行的多个请求,可以按请求顺序展示、关闭 Loading,但是在请求间隔时间很短的情况下,可能有损用户体验。
并行的多个请求,如果添加多个遮罩 Loading,每当一个请求返回相应后再关闭一个遮罩,这对用户体验来说是不友好的,应该保证全局只有一个遮罩,在最后一个请求完成后关闭。
#
需求实现#
自定义组件#
组件设计- 遮罩,局部遮罩(组件)或全屏遮罩(默认全屏)
- 全屏时,遮罩层绝对定位于浏览器窗口视图, position: fixed;
- 局部时,遮罩层绝对定位于组件,position: absolute,absolute 定位相对于最近的已定位祖先元素(在我们这里应该是目标挂载节点),如果没有已定位祖先元素则会默认以 document.body 为基准,所以设置根元素 position: relative,用 addClass 和 removeClass 方法实现
- 图层位于最上方,z-index 设置 999(但是这里其实没有完全保证最上方,不会吧,不会吧,不会有弹出框或提示框 Popover 的 z-index 比我还大吧?)
- 背景颜色可自定义,默认 rgba(0, 0, 0, .9)
- 遮罩内元素垂直、水平居中,用 flex 布局实现
- 动态旋转 icon ,可自定义 element-icon 如 ‘el-icon-loading',默认使用 CSS 动画实现的圆环旋转
- 文字提示,可自定义,默认为空
- 优化交互效果,给组件添加淡入、淡出的过渡效果。
样式代码:
Click to expand code
#
暴露服务接口Click to expand code
#
注册自定义指令一个指令定义对象可以提供如下几个钩子函数 (均为可选):
- bind:绑定触发,只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
- inserted:插入触发,被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
- update:更新触发,所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是可以通过比较更新前后的值来忽略不必要的模板更新。
- componentUpdated:组件更新触发,指令所在组件的 VNode 及其子 VNode 全部更新后调用。
- unbind:解绑触发,只调用一次,指令与元素解绑时调用。
指令钩子函数会被传入以下参数:
- el:指令所绑定的元素,可以用来直接操作 DOM。
- binding:一个对象,包含以下 property:
- name:指令名,不包括 v- 前缀。
- value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
- oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
- expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
- arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
- modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
- vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
- oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。
Click to expand code
#
单例模式为什么不直接调用 element-ui 的 Loading 组件呢?
以服务的方式调用的全屏 Loading 是单例的:若在前一个全屏 Loading 关闭前再次调用全屏 Loading,并不会创建一个新的 Loading 实例,而是返回现有全屏 Loading 的实例。
此时调用它们中任意一个的 close 方法都能关闭这个全屏 Loading,这并不符合我们预期对并行请求的处理。
在现有的很多示例里,用 state 存储 Loading 组件的 visible 状态, 但是我们的多页面应用,有多个独立的 Store,并不适合。
为了兼容 Loading 组件适用于其他情况,另外包装一层,保证只有一个 Loading 实例。