写在之前

实现 Loading 思路上并不困难,只不过是根据请求前后进行设置而已,可当要设置的状态越来越多又不能全局统一设置时,就又变得十分繁琐重复。在 Github 和各个社区站里搜 Loading 的插件,每个插件尽管样式上天差地别,但使用起来都没有太大的差别,要么给定 API,手动show,hide,要么封装了一个样式组件,还是需要手动判断什么时候该显示什么时候不该显示。

这个插件所想要解决的问题也是相对容易的自动进行 Loading 状态切换。

经过几天的改进,这个版本的插件已经基本符合预期,感谢 @TomVista @shintendo @lllllliu @luoway 在上一个帖子的指教~。

代码中有任何问题 /可以改进的地方都希望能指教一下~(*^▽^*)。

从使用方式说起

不管从 0 开始写起还是直接下载的 Loading 插件,都会抽象为一个组件,在用到的时候进行加载 Loading,或者通过 API 手动进行 show 或者 hide

...

this.$wait.show()

await fetch('http://example.org')

this.$wait.hide()

或者通过 Loading 状态进行组件间的切换

。要想注册成全局状态,还需要给 axios 类的网络请求包添加拦截器,然后设置一个全局 Loading 状态,每次有网络请求或者根据已经设置好的 URL 将 Loading 状态设置为加载,请求完成后在设置为完成。

注册 axios 拦截器:

let loadingUrls = [

`${apiUrl}/loading/`,

`${apiUrl}/index/`,

`${apiUrl}/comments/`,

...

]

axios.interceptors.request.use((config) => {

let url = config.url

if (loadingUrls.indexOf('url') !== -1) {

store.loading.isLoading = true

}

})

axios.interceptors.response.use((response) => {

let url = response.config.url

if (loadingUrls.indexOf('url') !== -1) {

store.loading.isLoading = false

}

})

使用时在每个组件下获取出 loading 状态,然后判断什么时候显示 loading,什么时候显示真正的组件。

...

components: {

loader

},

computed: {

isLoading: this.$store.loading.isLoading

},

async getMainContent () {

// 实际情况下 State 仅能通过 mutations 改变.

this.$sotre.loading.isLoading = false

await axios.get('...')

this.$sotre.loading.isLoading = false

},

async getMain () {

await getMainContent()

}

...

在当前页面下只有一个需要 Loading 的状态时使用良好,但如果在同一个页面下有多个不同的组件都需要 Loading,你还需要根据不同组件进行标记,好让已经加载完的组件不重复进入 Loading 状态...随着业务不断增加,重复进行的 Loading 判断足以让人烦躁不已...

整理思路

Loading 的核心很简单,就是请求服务器时需要显示 Loading,请求完了再还原回来,这个思路实现起来并不费力,只不过使用方式上逃不开上面的显式调用的方式。顺着思路来看,能进行 Loading 设置的地方有,

设置全局拦截,请求开始前设置状态为加载。

设置全局拦截,请求结束后设置状态为完成。

在触发请求的函数中进行拦截,触发前设置为加载,触发后设置为完成。

判断请求后的数据是否为非空,如果非空则设置为完成

最终可以实现的情况上,进行全局拦截设置,然后局部的判断是最容易想到也是最容易实现的方案。给每个触发的函数设置before和after看起来美好,但实现起来简直是灾难,我们并没有before和after这两个函数钩子来告诉我们函数什么时候调用了和调用完了,自己实现吧坑很多,不实现吧又没得用只能去原函数里一个个写上。只判断数据局限性很大,只有一次机会。

既然是即插即用的插件,使用起来就得突出一个简单易用,基本思路上也是使用全局拦截,但局部判断方面与常规略有不同,使用数据绑定(当然也可以再次全局响应拦截),咱们实现起来吧~。

样式

Loading 嘛,必须得有一个转圈圈才能叫 Loading,样式并不是这个插件的最主要的,相信各位大佬都能写的狂拽酷炫又典雅简约,这里就直接用 CSS 实现一个容易实现又不显得很糙的:

...

.loading {

width: 50px;

height: 50px;

border: 4px solid rgba(0,0,0,0.1);

border-radius: 50%;

border-left-color: red;

animation: loading 1s infinite linear;

}

@keyframes loading {

0% { transform: rotate(0deg) }

100% { transform: rotate(360deg) }

}

固定大小 50px 的正方形,使用border-radius把它盘得圆润一些,border设置个进度条底座,border-left-color设置为进度条好了。

绑定数据与 URL

提供外部使用接口

上面思路中提到,这个插件是用全局拦截与数据绑定制作的:

暴露一个 source 属性,从使用的组件中获取出要绑定的数据。

暴露一个 urls 属性,从使用的组件中获取出要拦截的 URL。

...

export default {

props: {

source: {

require: true

},

urls: {

type: Array,

default: () => { new Array() }

}

},

data () {

return { isLoading: true }

},

watch: {

source: function () {

if (this.source) {

this.isLoading = false

}

}

}

}

....

不用关心 source 是什么类型的数据,我们只需要监控它,每次变化时都将 Loading 状态设置为完成即可,urls 我们稍后再来完善它。

设置请求拦截器

拦截器中需要的操作是将请求时的每个 URL 压入一个容器内,请求完再把它删掉。

Vue.prototype.__loader_checks = []

Vue.prototype.$__loadingHTTP = new Proxy({}, {

set: function (target, key, value, receiver) {

let oldValue = target[key]

if (!oldValue) {

Vue.prototype.__loader_checks.forEach((func) => {

func(key, value)

})

}

return Reflect.set(target, key, value, receiver)

}

})

axios.interceptors.request.use(config => {

Vue.prototype.$__loadingHTTP[config.url] = config

return config

})

axios.interceptors.response.use(response => {

delete Vue.prototype.$__loadingHTTP[response.config.url]

return response

})

将其挂载在 Vue 实例上,方便我们之后进行调用,当然还可以用 Vuex,但此次插件要突出一个依赖少,所以 Vuex 还是不用啦。

直接挂载在 Vue 上的数据不能通过computed或者watch来监控数据变化,咱们用Proxy代理拦截set方法,每当有请求 URL 压入时就做点什么事。Vue.prototype.__loader_checks用来存放哪些实例化出来的组件订阅了请求 URL 时做加载的事件,这样每次有 URL 压入时,通过Proxy来分发给订阅过得实例化 Loading 组件。

订阅 URL 事件

...

export default {

props: {

source: {

require: true

},

urls: {

type: Array,

default: () => { new Array() }

}

},

data () {

return { isLoading: true }

},

watch: {

source: function () {

if (this.source) {

this.isLoading = false

}

}

},

mounted: function () {

if (this.urls) {

this.__loader_checks.push((url, config) => {

if (this.urls.indexOf(url) !== -1) {

this.isLoading = true

}

})

}

}

}

....

每一个都是一个崭新的实例,所以直接在 mounted 里订阅 URL 事件即可,只要有传入urls,就对__loader_checks里每一个订阅的对象进行发布,Loader 实例接受到发布后会判断这个 URL 是否与自己注册的对应,对应的话会将自己的状态设置回加载,URL 请求后势必会引起数据的更新,这时我们上面监控的source就会起作用将加载状态设置回完成。

使用槽来适配原来的组件

写完上面这些你可能有些疑问,怎么将 Loading 时不应该显示的部分隐藏呢?答案是使用槽来适配,

export default {

props: {

source: {

require: true

},

urls: {

type: Array,

default: () => { new Array() }

}

},

data () {

return { isLoading: true }

},

watch: {

source: function () {

if (this.source) {

this.isLoading = false

}

}

},

mounted: function () {

if (this.urls) {

this.__loader_checks.push((url, config) => {

if (this.urls.indexOf(url) !== -1) {

this.isLoading = true

}

})

}

}

}

....

还是通过isLoading判断,如果处于加载那显示转圈圈,否则显示的是父组件里传入的槽,

这里写的要注意,Vue 这里有一个奇怪的 BUG,

在有时,如果同级的标签同时出现v-if与CSS 选择器且样式是scoped,那用CSS 选择器设置的样式将会丢失,

如果没有设置key那.loading的样式会丢失,除了设置key还可以把它变成嵌套的

注册成插件

Vue 中的插件有四种注册方式,这里用mixin来混入到每个实例中,方便使用,同时我们也把上面的 axios 拦截器也注册在这里。

import axios

import Loader from './loader.vue'

export default {

install (Vue, options) {

Vue.prototype.__loader_checks = []

Vue.prototype.$__loadingHTTP = new Proxy({}, {

set: function (target, key, value, receiver) {

let oldValue = target[key]

if (!oldValue) {

Vue.prototype.__loader_checks.forEach((func) => {

func(key, value)

})

}

return Reflect.set(target, key, value, receiver)

}

})

axios.interceptors.request.use(config => {

Vue.prototype.$__loadingHTTP[config.url] = config

return config

})

axios.interceptors.response.use(response => {

delete Vue.prototype.$__loadingHTTP[response.config.url]

return response

})

Vue.mixin({

beforeCreate () {

Vue.component('v-loader', Loader)

}

})

}

}

使用

在入口文件中使用插件

import Loader from './plugins/loader/index.js'

...

Vue.use(Loader)

...

任意组件中无需导入即可使用

{{ msg }}

根据绑定的数据和绑定的 URL 自动进行 Loading 的显示与隐藏,无需手动设置isLoading是不是该隐藏,也不用调用show与hide在请求的方法里打补丁。

其他

上面的通过绑定数据来判断是否已经响应,如果请求后的数据不会更新,那你也可以直接在 axios 的 response 里做拦截进行订阅发布模式的响应。

最后

咳咳,又到了严(hou)肃(yan)认(wu)真(chi)求 Star 环节了,附上完整的项目地址(我不会告诉你上面的测试地址里的代码也很完整的,绝不会!)。

vue连线 插件_一起写一个即插即用的 Vue Loading 插件相关推荐

  1. html怎么引轮播图插件,原生js写一个无缝轮播图插件(支持vue)

    轮播图插件(Broadcast.js) 前言:写这个插件的原因 前段时间准备用vue加上网易云的nodejs接口,模拟网易云音乐移动端.因为想自己写一遍所有的代码以及加固自己的flex布局,所以没有使 ...

  2. java 广告插件_徒手创建一个chrome扩展-屏蔽广告插件

    创建一个文件夹,创建以下文件 maniftest.json  background.js和 icon图片 maniftest.json文件设置如下 { "name": " ...

  3. vue如何使用原生js写动画效果_原生js写一个无缝轮播图插件(支持vue)

    轮播图插件(Broadcast.js) 前言:写这个插件的原因 前段时间准备用vue加上网易云的nodejs接口,模拟网易云音乐移动端.因为想自己写一遍所有的代码以及加固自己的flex布局,所以没有使 ...

  4. 正则表达式限定长度_自己写一个通用的邮箱正则表达式

    今天把正则又复习了一遍,为了加深记忆,自己写一个邮箱的正则表达式 咱们先来看几个合法的邮箱地址 hd33322@nat123.com maksim.kim.82@d-link.ua vova_laza ...

  5. 未能加载文件或程序集或它的某一个依赖项_手写一个miniwebpack

    前言 之前好友希望能介绍一下 webapck 相关的内容,所以最近花费了两个多月的准备,终于完成了 webapck 系列,它包括一下几部分: webapck 系列一:手写一个 JavaScript 打 ...

  6. golang 热插拨 插件_从零开始实现一个插件化框架(一)

    欢迎关注专栏:里面定期分享Android和Flutter架构技术知识点及解析,还会不断更新的BATJ面试专题,欢迎大家前来探讨交流,如有好的文章也欢迎投稿. Flutter跨平台开发终极之选​zhua ...

  7. 正则邮箱_自己写一个通用的邮箱正则表达式

    今天把正则又复习了一遍,为了加深记忆,自己写一个邮箱的正则表达式 咱们先来看几个合法的邮箱地址 hd33322@nat123.com maksim.kim.82@d-link.ua vova_laza ...

  8. 手写一个promise用法_手写一个 Promise

    1 js 的基本数据类型? 2 JavaScript 有几种类型的值? 3 什么是堆?什么是栈?它们之间有什么区别和联系? 4 内部属性 [Class] 是什么? 5 介绍 js 有哪些内置对象? 6 ...

  9. 用python写一个alfred workflow百度翻译插件

    用python写一个workflow百度翻译插件 最近在学习python,同事看到了说他有经常翻译多国语言的需求,看我能不能帮他写一个支持多国语言翻译的workflow插件,这就有了它. 功能介绍 打 ...

最新文章

  1. C# 将数据写入PDF文档
  2. Effective C++笔记(一)——条款26-29
  3. request 获取各种路径
  4. 2019第一篇万字长文!30+家一线投资机构已出投资新策略...
  5. SAP Spartacus org unit list和unit detail的分屏显示原理
  6. android中解析后台返回的json字符串
  7. Linux shell逐行读取文件的方法-比较
  8. linux ubuntu技术支持电话,Ubuntu 17.04 将在1月13日结束技术支持
  9. 大数据每日哔哔、新征程
  10. OpenCv之图像二值化(笔记12)
  11. java 有界泛型_JAVA泛型中的有界类型(extends super)
  12. Python url的编码转换
  13. iOS SwiftUI ☞ UIKit框架的封装使用
  14. 微软CEO纳德拉:坚信Win10通用应用平台才是正道
  15. INTERVAL 用法 mysql
  16. 阅读器android工程,一种简单的纯粹——全球首款 EINK屏 安卓手机 BOOX E43 工程机测试体验...
  17. 【ESP 保姆级教程】疯狂传感器篇 —— 案例:ESP8266 + BH1750 + webserver(局域网内查看曲线变化图)
  18. 中国工业大数据行业发展趋势分析与投资战略规划建议报告2022-2028年版
  19. AE效果:过时与抠像
  20. NXP-MPC5748G车载MCU使用(食用)方法(踩坑)实用指南(骗人教程)(一):创建工程与导入

热门文章

  1. java发送文本邮件_1、java实现发送纯文本邮件
  2. git mysql差异备份_结合Git实现Mysql差异备份,可用于生产环境
  3. C#关键字详解第一节
  4. 2018-2019-1 20165214 《信息安全系统设计基础》第八周学习总结
  5. Manifold learning-based methods for analyzing single-cell RNA-sequencing data
  6. 模仿Bootstrap插件的放大镜插件
  7. MANIFEST.MF的用途(转载)
  8. c++ lamda表达式调用自身实现定时器
  9. 【转】jquery ui中文说明(使用方法)
  10. (24)System Verilog多个线程间通信(信箱)