为什么需要v-loading
界面在加载时,通常需要一个loading效果,一般的做法是,引入一个loading组件,通过v-show 来控制loading组件的展示。 但是这种做法略有繁琐,每个新的组件,都需要手动引入一遍loading组件,所以不如用一个自定义指令:v-loading,来实现这种效果。
实现原理:
-
查看 vue文档,我们看到在自定义指令中,有这两个钩子函数:inserted(),update();
-
他们都可以传入 v-loading所在的dom节点:el;控制v-loading指令的属性:binding.
-
首先创建一个新的Vue实例,该实例上有loading组件,在需要loading时,将该实例所在的dom节点,挂载到相应的节点(第二点中的el)上,在不需要loading时,将该实例从el中移除。
开写
1. 先写一个loading组件:loading.vue
<template>
<div class="loading">
<div class="loading-content">
<img src="~@assets/img/loading/loading.gif" width="50" height="50" />
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
data() {
return {
}
},
methods: {}
})
</script>
<style scoped lang="scss">
.loading {
//使用绝对定位,使loading的gif居中,这就要求父组件需要是相对定位
position: absolute;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
.loading-content {
text-align: center;
}
}
</style>
2.v-loading的指令逻辑: directive.ts
import Vue from 'vue'
import Loading from '@components/common/loading/loading'
function append(el: any) {
el.appendChild(el.instance.$el)
}
function remove(el: any) {
el.removeChild(el.instance.$el)
}
const loadingDirective = {
//在这个钩子函数中,先生成一个新的Vue实例,并把它挂载到一个div标签上
inserted(el: any, binding: any) {
const app = new Vue(Loading)
const instance = app.$mount(document.createElement('div'))
//把 vue实例挂载的div保存下来,保存到el中(需要保证el中原来没有instance这个属性,否则需要换个名字)
el.instance = instance
//如果loading为true,则把div通过appendChild方法,增加到父标签上
if (binding.value) {
append(el)
}
},
update(el: any, binding: any) {
//如果loading值发生改变
if (binding.value !== binding.oldValue) {
//如果需要loading,那么通过appendChild方法 把div(vue实例之前保存在了el.instance上,需要添加/移除的是vue实例的挂载节点,即div,el.instance.$el)加上去
if (binding.value) {
append(el)
} else {
//如果loading===false,那么通过removeChild把div移除
remove(el)
}
}
}
}
export default loadingDirective
3.在 App.vue 中,全局注册v-loading指令
import loadingDirective from '@components/common/loading/directive'
...
...
Vue.directive('loading', loadingDirective)
4.在其他vue组件中就可以使用 v-loading来控制了。
...
<Comp v-loading = "loading"></Comp>