在项目中我们通常会使用一个loading的状态去控制页面渲染之前的状态,在Element-UI中只需要v-loading="loading",loading只需要绑定
Boolean
即可.但是其中是怎么实现的需要自己去主动思考。
自定义指令
v-for、v-if、v-model这些都是vue自带的指令,vue的实例主要用于数据绑定、事件监听、dom更新,这些指令主要就是去操作dom。 自定义指令分两种:
- 全局指令
- 局部指令
指令选项
1.钩子函数
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
2.钩子函数参数
指令钩子函数会被传入以下参数:
el:指令所绑定的元素,可以用来直接操作 DOM。
binding:一个对象,包含以下 property: name:指令名
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 }
。
实现v-load
首先在src文件夹下创建一个directive文件夹去存放loading相关的代码,在loading.vue文件中去写相关的样式操作
<template>
<div v-show="visible" class="loading-wrap">
<div class="loading-box">
<div class="loading-add"></div>
<div class="loading-txt">加载中...</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
visible:false
}
}
}
</script>
<style lang="less" scoped>
.loading-wrap{
white-space: nowrap;
}
.loading-box{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%);
font-size: 16px;
white-space: nowrap;
user-select: none;
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.loading-add{
width: 30px;
height: 30px;
border: 2px solid #F53939 ;
border-top-color: transparent;
border-radius: 100%;
animation: add infinite 0.75s linear;
}
@keyframes add {
0% {
transform: rotateZ(0);
}
100% {
transform: rotateZ(360deg);
}
}
.loading-txt {
margin-top: 5px;
color: #F53939 ;
}
</style>
同级目录去创建一个loading.js
import Vue from "vue";
import Loading from "./loading.vue";
const Mask = Vue.extend(Loading);
const toggleLoading = (el, binding) => {
if(binding.value) {
Vue.nextTick(() => {
// 控制loading组件显示
el.instance.visible = true;
// 插入到目标元素
insertDom(el, el, binding)
})
}else{
el.instance.visible = false;
}
}
const insertDom = (parent, el) => {
parent.appendChild(el.mask)
}
export default {
// bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
bind: function (el,binding,vnode) {
// new Mask()的时候,把该组件实例挂载到一个div上
const mask = new Mask({
el: document.createElement('div'),
data() {return {}}
})
el.instance = mask;
el.mask = mask.$el;
binding.value && toggleLoading(el,binding)
},
update: function (el, binding) {
if(binding.oldValue !== binding.value) {
toggleLoading(el, binding)
}
},
unbind: function (el, binding) {
el.instance && el.instance.$destroy()
}
}
Vue.extend 返回一个构造器,new该构造器可以返回一个组件实例,new Mask()的时候,把该组件实例挂载到一个div上。 在bing函数先去打印一下el看一下都是什么:
发现是一个空的div,此时需要用一个变量去接住这个实例,el.instance = mask,- 接下来判断 value 是否为 true ,如果是 true 则执行 toggleLoading ,toggleLoading 方法用来控制是否显示 loading.vue 组件中的 visible变量,并且如果 value是true则插入到目标元素。
在directive文件夹下创建index.js
import loading from './loading';
export default {
install(Vue) {
Vue.directive("loading",loading)
}
}
main.js中去全局注册:
import loading from './directives/loading'
Vue.use(loading)
项目中去使用:
<div v-load="true"></div>
让我们来来看一下效果: