Vue2+TS 实现自定义指令: v-loading

1,816 阅读1分钟

为什么需要v-loading

界面在加载时,通常需要一个loading效果,一般的做法是,引入一个loading组件,通过v-show 来控制loading组件的展示。 但是这种做法略有繁琐,每个新的组件,都需要手动引入一遍loading组件,所以不如用一个自定义指令:v-loading,来实现这种效果。

实现原理:

  1. 查看 vue文档,我们看到在自定义指令中,有这两个钩子函数:inserted(),update();

  2. 他们都可以传入 v-loading所在的dom节点:el;控制v-loading指令的属性:binding.

  3. 首先创建一个新的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>