Handwritten vUE custom directive (Directive) V-loading, project combat

Goal:

  1. Used in vUE project tagsv-loading="true"As shown by using v-loading=”true” in Element-UI.
  2. After learning it, you can directly copy it to your project for actual combat. V-loading will definitely be used in the VUE project at work, which is super convenient to introduce global use at a time

Here I do two laoding effects:

  1. Full screen loading effect, used when entering the page to use loaidng
  2. Partial load effect, load effect when the user paginates or loads part of the content

Display effect:

Full screen loading:<div v-loading="true"></div>

Local loading:<div v-load="true"></div>


The directory structure

Loading 5 files was created, copied into the loading file, and then used in main.js. Can't work can leave a message | - SRC | -- App. Vue | -- main. Js / / need to use | - components | | -- the HelloWorld. Vue | - directive / / need to use | | - loading . | | - index js | | - load. The local load js / / | | - load. The vue / / local load | | -- loading. Js / / global loading | | - loading. The vue / / Global loadingCopy the code

1. Create a file

Js; load.js; load.vue; load.js; load.vue; load.vue; load.vue With vue. directive, you can v-load pages with custom directives

loading/index.js

import load from './load';
import loading from './loading';
export default {
  install(Vue) {
    Vue.directive("load", load), 	  / / partial load
    Vue.directive("loading", loading) / / global loading}}Copy the code

loading/loading.vue

Is a component used to insert into the target element of a custom directive, where you can write some loading styles

// 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">Global loading...</div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      visible: false}; }};</script>
<style scoped>
.loading-wrap {
  position: absolute;
  left: 0 ! important;
  right: 0 ! important;
  top: 0 ! important;
  bottom: 0 ! important;
  width: 100vw ! important;
  height: 100vh ! important;
  background: rgba(0.0.0.0.7);
  white-space: nowrap;
}
.loading-box {
  user-select: none;
  font-size: 16px;
  white-space: nowrap;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
}
.loading-add {
  width: 16px;
  height: 4px;
  background-color: #eee;
  display: inline-block;
  margin-right: 4px;
  position: relative;
  animation: add 1s 0s linear infinite forwards;
  border-radius: 4px;
}
.loading-add::after {
  content: "";
  display: inline-block;
  width: 16px;
  height: 4px;
  background-color: #eee;
  position: absolute;
  left: 50%;
  top: 50%;
  border-radius: 4px;
  transform: translate(-50%, -50%) rotateZ(90deg);
}

@keyframes add {
  0% {
    transform: rotateZ(0);
  }

  100% {
    transform: rotateZ(360deg); }}.loading-txt {
  color: #eee;
  animation: fontColor 3s 0s linear infinite reverse;
  display: inline-block;
}

@keyframes fontColor {
  0% {
    color: rgba(238.238.238.0.85);
  }
  25% {
    color: rgba(135.207.235.0.85);
  }
  50% {
    color: rgba(255.0.0.0.85);
  }
  75% {
    color: rgba(51.51.51.0.85);
  }
  100% {
    color: rgba(255.255.0.0.85); }}</style>
Copy the code

loading/loading.js

// directive/loading/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(() = > {
            // Controls the loading component display
            el.instance.visible = true
            // Insert to the target element
            insertDom(el, el, binding)
        })
    } else {
        el.instance.visible = false}}const insertDom = (parent, el) = > {
    parent.appendChild(el.mask)
}

export default {
    bind: function (el, binding, vnode) {
        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 (el, binding) {
        el.instance && el.instance.$destroy()
    }
}
Copy the code

loading/load.js

// directive/loading/load.js
import Vue from 'vue';
import Load from './load.vue';

const Mask = Vue.extend(Load);

const toggleLoading = (el, binding) = > {
    if (binding.value) {
        Vue.nextTick(() = > {
            el.instance.visible = true// Controls the loading component display
            insertDom(el, el, binding)// Insert to the target element})}else {
        el.instance.visible = false}}const insertDom = (parent, el) = > {
    parent.appendChild(el.mask)
}

export default {
    // bind(){} when the instruction is bound
    bind: function (el, binding, vnode) {
        const mask = new Mask({
            el: document.createElement('div'),
            data() { }
        })
        el.instance = mask
        el.mask = mask.$el
        el.maskStyle = {}
        binding.value && toggleLoading(el, binding)
    },
    // update(){} This function is triggered when data is updated
    update: function (el, binding) {
        if(binding.oldValue ! == binding.value) { toggleLoading(el, binding) } },// unbind(){} triggers this function when it is unbound
    unbind: function (el, binding) {
        el.instance && el.instance.$destroy()
    }
}
Copy the code

loading/load.vue

// directive/loading/load.vue
<template>
  <div v-show="visible" class="load-wrap">
    <div class="loading-box">
      <div class="loading-add"></div>
      <div class="loading-txt">Loading in...</div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      visible: false}; }};</script>
<style scoped>
.load-wrap {
  white-space: nowrap;
}
.loading-box {
  user-select: none;
  display: flex;
  align-items: center;
  justify-content: center;
}
.loading-add {
  width: 16px;
  height: 4px;
  background-color: #eee;
  display: inline-block;
  margin-right: 4px;
  position: relative;
  animation: add 1s 0s linear infinite reverse;
  border-radius: 4px;
}
.loading-add::after {
  content: "";
  display: inline-block;
  width: 16px;
  height: 4px;
  background-color: #eee;
  position: absolute;
  left: 50%;
  top: 50%;
  border-radius: 4px;
  transform: translate(-50%, -50%) rotateZ(90deg);
}

@keyframes add {
  0% {
    transform: rotateZ(0);
  }

  100% {
    transform: rotateZ(-360deg); }}.loading-txt {
  color: #eee;
  display: inline-block;
  font-size: 16px;
  animation: fontColor 3s 0s linear infinite reverse forwards;
}
@keyframes fontColor {
  0% {
    color: rgba(238.238.238.0.85);
  }
  25% {
    color: rgba(135.207.235.0.85);
  }
  50% {
    color: rgba(255.0.0.0.85);
  }
  75% {
    color: rgba(51.51.51.0.85);
  }
  100% {
    color: rgba(255.255.0.0.85); }}</style>

Copy the code
  • Vue.extend takes arguments and returns a constructor. New This constructor can return a component instance.
  • When we new Mask(), we mount the component instance to a div that has not yet been mounted to the page. Print it out.
  • Then catch the mask instance with a variable el.instance = mask
  • The toggleLoading method controls whether the visible variable in loading. Vue is displayed. And if value is true, it is inserted into the target element


Function attributes

  • Bind: Called only once, the first time a directive is bound to an element. This is where you can perform one-time initialization Settings
  • Inserted: Called when the bound element is inserted into the parent node
  • Update: called when the VNode of the component is updated.
  • ComponentUpdated: Invoked when the VNode of the component where the directive resides and its child VNodes are all updated
  • Unbind: Called only once, when an instruction is unbound from an element.

Function parameters

  • El: The element bound by the directive that can be used to manipulate the DOM directly.
  • Binding: Contains event information
  • Vnode: virtual node generated by Vue compilation.
  • OldNode: Last virtual node, available only in update and componentUpdated hooks.

Binding contains:

  • Name: indicates the command name
  • Value: binding value Example:v-my-directive="1 + 1", the binding value is 2.
  • OldValue: indicates the value before binding. Only in theupdatecomponentUpdatedHooks are available. Available regardless of whether the value changes.
  • Expression: command expression in the form of a string. For example,v-my-directive="1 + 1"In, the expression is “1 + 1”.
  • Arg Is an optional parameter passed to the instruction. For example,v-my-directive:foo, the parameter is “foo”.
  • Modifiers: An object that contains modifiers. Such as:v-my-directive.foo.bar, the modifier object is{ foo: true, bar: true }.


Case use teaching:

According to the directory structure above, the corresponding file code copy into, can be used

src/main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store' 

// Import loading (including global loading and local loading)
import loading from './directive/loading' 
Vue.use(loading);

Vue.config.productionTip = false
new Vue({
  router,
  store,
  render: h= > h(App)
}).$mount('#app')
Copy the code

src/components/HelloWorld.vue

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <! -- Global loading true -- display false -- hide -->
    <div v-loading="true"> </div>

 	<! -- local load true -- display false -- hide -->
    <div v-load="false"> </div>
  </div>
</template>
<script>
export default {
  name: "HelloWorld".props: {
    msg: String,}};</script>
<style scoped></style>	
Copy the code

Global loading:

Partial load: