想必用过 element-ui 来开发中后台项目的同学都知道,其内置的 v-loading 指令是非常友好,只需要提供一个 Boolean 值就能实现加载动画的一个指令,如果使用过 element-ui,但没有使用过 v-loading 指令的同学,不妨先了解一下基本用法
本文会通过 源码剖析、思路分析以及简单实现三个步骤,来简单呈现其实现的原理。
源码剖析
首先打开 element-ui 项目目录,定位到 v-loading 主文件
import directive from './src/directive'; // loading指令实现
import service from './src/index'; // loading服务方式实现
export default {
install(Vue) {
Vue.use(directive);
Vue.prototype.$loading = service;
},
directive,
service
};
复制代码
此文件对外暴露了三个属性,分别是 install函数、directive指令实现以及service服务方式实现
此文件会被 element组件入口文件 引入, 并且把 directive指令 全局注册了一遍以及在 Vue
原型上扩展了 $loading
方法
// line 156
Vue.use(Loading.directive);
// line 163
Vue.prototype.$loading = Loading.service;
复制代码
Loading 指令实现
Vue.js 的插件应该有一个公开方法 install
。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象:
MyPlugin.install = function (Vue, options) {
// 1. 添加全局方法或属性
Vue.myGlobalMethod = function () {
// 逻辑...
}
// 2. 添加全局资源
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// 逻辑...
}
...
})
// 3. 注入组件
Vue.mixin({
created: function () {
// 逻辑...
}
...
})
// 4. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
// 逻辑...
}
}
复制代码
思路分析
其实 v-loading
实现思路很简单,且听我一一道来。
Vue.directive
内置了五个钩子函数 bind(绑定触发)
、inserted(插入触发)
、update(更新触发)
、componentUpdated(组件更新触发)
和unbind(解绑触发)
当需要绑定 v-loading
生成好后,我们可以根据绑定的 Boolean 值,来控制显隐
那么如何生成loading效果遮罩层呢?element-ui的做法是 利用Vue.extend扩展loading组件,实时计算其样式值,并且把扩展的实例挂载到钩子函数的el参数中,以达到改变组件状态的目的
简单实现
接下来我将简单实现一遍 v-loading
的核心功能,来帮助大家更好的掌握
首先,我们需要定义一个 loading
组件:
<template>
<transition name="loading-fade">
<div
v-show="visible"
class="loading-mask"
>
loading
</div>
</transition>
</template>
<script>
export default {
name: 'Loading',
data() {
return {
visible: false
};
}
};
</script>
复制代码
注意看这里 visible
,这个属性就是来控制整个loading组件显隐的
当我们指令绑定之后,就需要对绑定的 value
值进行监听
import Vue from 'vue';
import Loading from './loading';
// Loading构造函数
const Mask = Vue.extend(Loading);
const loadingDirective = {};
loadingDirective.install = Vue => {
// 切换组件状态函数
const toggleLoading = (el, binding) => {
if (binding.value) {
Vue.nextTick(() => {
insertDom(el, el, binding);
});
}
else {
el.instance.visible = false;
}
};
// 插入Loading
const insertDom = (parent, el) => {
parent.appendChild(el.mask);
el.instance.visible = true;
};
Vue.directive('loading', {
bind: function(el, binding) {
/**
* 这里把Loading组件实例挂载到el上,然后再把el传参到toggleLoading中判断
*/
const mask = new Mask({
el: document.createElement('div'),
data: {}
});
el.instance = mask;
el.mask = mask.$el;
el.maskStyle = {};
binding.value && toggleLoading(el, binding);
},
update: function(el, binding) {
if (binding.oldValue !== binding.value) {
toggleLoading(el, binding);
}
},
unbind: function() {
// destory
}
});
};
export default loadingDirective;
复制代码
Loading 指令
的具体实现在此 文件