SoFunction
Updated on 2025-03-10

Detailed explanation of the example of Vue3 implementing global loading instruction

Preface

  • When the company was developing component libraries, I was encapsulatingloadingThis component requires a global useloading, it can be done with plug-ins, but the project development cycle is not too tight, so I decided to write one by myself.
  • Write a globalcomponnetsIt's OK, but it needs to be imported and registered on each page used, becauseloadingThere will be many places to use, so I feel that it needs to be implemented in a more elegant way.

1. Complete the loading component

<template>
  <div class="mask">
    <div class="loader">
      <div>
        <div class="dot"></div>
        <div class="dot"></div>
        <div class="dot"></div>
        <div class="dot"></div>
        <div class="dot"></div>
      </div>
      <div class="tip-text">{{ title }}</div>
    </div>
  </div>
</template>

<script setup>
import { ref, defineExpose } from 'vue'

let title = ref('') // Load prompt text // Change the load prompt text const setTitle = (val) => {
     = val
}
    
 // Expose it defineExpose({
     title,
     setTitle
 })
}
</script>

<style lang="less" scoped>
.mask {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  background-color: rgba(0, 0, 0, 0.8);
  z-index: 99;
  .loader {
    position: absolute;
    top: 50%;
    left: 40%;
    margin-left: 10%;
    transform: translate3d(-50%, -50%, 0);
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  .tip-text {
    color: #3793ff;
    padding-top: 10px;
  }
  .dot {
    width: 18px;
    height: 18px;
    border-radius: 100%;
    display: inline-block;
    animation: slide 1s infinite;
  }
  .dot:nth-child(1) {
    animation-delay: 0.1s;
    background: #1fbfff;
  }
  .dot:nth-child(2) {
    animation-delay: 0.2s;
    background: #2ea4ff;
  }
  .dot:nth-child(3) {
    animation-delay: 0.3s;
    background: #3793ff;
  }
  .dot:nth-child(4) {
    animation-delay: 0.4s;
    background: #3b89ff;
  }
  .dot:nth-child(5) {
    animation-delay: 0.5s;
    background: #4577ff;
  }
}

@-moz-keyframes slide {
  0% {
    transform: scale(1);
  }
  50% {
    opacity: 0.3;
    transform: scale(2);
  }
  100% {
    transform: scale(1);
  }
}
@-webkit-keyframes slide {
  0% {
    transform: scale(1);
  }
  50% {
    opacity: 0.3;
    transform: scale(2);
  }
  100% {
    transform: scale(1);
  }
}
@-o-keyframes slide {
  0% {
    transform: scale(1);
  }
  50% {
    opacity: 0.3;
    transform: scale(2);
  }
  100% {
    transform: scale(1);
  }
}
@keyframes slide {
  0% {
    transform: scale(1);
  }
  50% {
    opacity: 0.3;
    transform: scale(2);
  }
  100% {
    transform: scale(1);
  }
}
</style>

2. Create a new file to write custom instruction logic for loading

const loadingDirective = { 
    mounted(el,binding) { 
    },
    updated(el,binding) {
    },
}
return loadingDirective  // Export

CreatedloadingDirectiveIn the object, write two hook functions because we want to operate on these two lifecycle functions.

2.1 Create the DOM corresponding to this component

  • Create a new vue instance, and then mount it dynamically. After mounting, we can get thisDOMAn example of

See the following code block for specific implementation

import { createApp } from 'vue' // Import createApp methodimport Loading from './loading' // Import the loading component we wroteconst loadingDirective = {
    mounted(el, binding) {
        // Create app objects and components to loading components written for us        const app = createApp(Loading)
        // Dynamically create a div node and mount the app on the div        // Our loading component will replace the innerHTML of this div tag        const instance = (('div'))
    },
    updated(el, binding) {},
}
return loadingDirective  // Export

2.2 Two new methods are added to the file, namely inserting nodes and removing nodes.

import { createApp } from 'vue' // Import createApp methodimport Loading from './loading' // Import the loading component we wroteconst loadingDirective = {
    mounted(el, binding) {
        // Create app objects and components to loading components written for us        const app = createApp(Loading)
        
        // Dynamically create a div node and mount the app on the div        // Our loading component will replace the innerHTML of this div tag        const instance = (('div'))
        
        // Because instance is also required to be used in updated, add instance to el        // In updated, you can access it through         = instance
        
        // The value passed by v-loading is stored in        if () {
            append(el)
        }
    },
    updated(el, binding) {
       remove(el)
    },
}
return loadingDirective  // Export
// Insert nodefunction append(el) {
    // Insert a dynamically created div node into the el node, and the content is our loading component    (.$el)  
}
// Remove nodefunction remove(el) {
    // Remove dynamically created div nodes    (.$el)
}

2.3 Improve updated period function

Insert the node at the beginning, trigger update when the value of v-loading changes. We remove the node, but writing this way is inappropriate. We need to improve the operations in updated.

updated (el, binding) {
    // If the value of the value changes, then we will judge and operate    if ( !== ) {
      // ternary expression true insert false remove       ? append(el) : remove(el)
    }

The basic instructions have been completed almost.

2.4 Solve three existing problems

  • Problems with positioning, if it is mountedDOMThe element has positioning attributes, so there is no problem, but it is mountedDOMWhen an element is not positioned, then its own absolute positioning may cause problems.
  • Page scrolling problem, ifloadingEffect: The page behind the mask layer will scroll. When scrolling the mask layer, the bottom page will scroll along.
  • To load text requirements, we can also add a piece of text to display it. The text will be different when waiting in different places.

2.4.1 Let’s solve the first and second problems first

  • When inserting a node, determine whether the mounted node has positioning. If not, add relative positioning to it.
  • Add prohibit/hide scrolling to nodes when inserting them.
  • Remove the class name when removing the node.
const relative = 'g-relative' // g-relative global relative positioning style nameconst hidden = 'g-hidden' // g-hidden Globally prohibited/hidden scrolling style name// Insert nodefunction append(el) {
const style = getComputedStyle(el)
    (hidden) // Add class name  if (['absolute', 'relative', 'fixed'].indexOf() === -1) {
    (relative) // Add class name  }
    // Insert a dynamically created div node into the el node, and the content is our loading component    (.$el)
}
// Remove nodefunction remove(el) {
    // Remove dynamically created div nodes    (.$el)
    (relative) // Remove class name    (hidden) // Remove class name}

g-relative g-hiddenIt's the style I wrote in the global css file

.g-relative {
    position: relative;
}
.g-hidden {
    overflow: hidden;
}

2.4.2 Finally, solve the third problem, dynamically display the loaded text.

Used in places:[]Syntax of

<template>
  <div class="demo" v-loading:[title]="loading"></div>
</template>
<script setup>
import { ref } from 'vue'

const title = ref('Fastly loading...')
const loading = ref(true)
}
</script>
  • IncomingtitleAfter that, you need to receive and insert ittitleexistloadingmiddle
  • passCan be received:[]The value passed
import { createApp } from 'vue' // Import createApp methodimport Loading from './loading' // Import the loading component we wroteconst loadingDirective = {
    mounted(el, binding) {
        // Create app objects and components to loading components written for us        const app = createApp(Loading)
        
        // Dynamically create a div node and mount the app on the div        // Our loading component will replace the innerHTML of this div tag        const instance = (('div'))
        
        // Because instance is also required to be used in updated, add instance to el        // In updated, you can access it through         = instance
        
        // The value passed by v-loading is stored in        if () {
            append(el)
        }
        
        // Here we determine whether there is a title value        if ( !== 'undefined') {
         // setTitle lets us define the method in the loading component          ()  
        }
    },
    updated (el, binding) {
        // Here we determine whether there is a title value        if ( !== 'undefined') {
         // setTitle lets us define the method in the loading component          ()  
        }
        
        // If the value of the value changes, then we will judge and operate        if ( !== ) {
          // ternary expression true insert false remove           ? append(el) : remove(el)
        }
     }
}
return loadingDirective  // Export
const relative = 'g-relative' // g-relative global relative positioning style nameconst hidden = 'g-hidden' // g-hidden Globally prohibited/hidden scrolling style name// Insert nodefunction append(el) {
const style = getComputedStyle(el)
    (hidden) // Add class name  if (['absolute', 'relative', 'fixed'].indexOf() === -1) {
    (relative) // Add class name  }
    // Insert a dynamically created div node into the el node, and the content is our loading component    (.$el)
}
// Remove nodefunction remove(el) {
    // Remove dynamically created div nodes    (.$el)
    (relative) // Remove class name    (hidden) // Remove class name}

3. Introduce registration in the file

If you want to use this custom directive globally, then we need toIntroduce registration in the file.

import { createApp } from 'vue'
import App from './'
import '@/assets/scss/' // Introduce global style filesimport loadingDirective from '@/views/loading/directive' //Introduce loading custom commandcreateApp(App)
  .directive('loading', loadingDirective)  // Global registration loading command  .mount('#app')

4. Use custom loading directives in the page

existonMountedWhen judgingloadingWhether it is displayed, before making a judgment, let’s see if we are writingloadingAfter customizing the directive, how to use it in the page.

<template>
  <div v-loading="loading"></div> // Just write v-loading='loading' on the node, the loading behind is a value</template>
<script setup>
import { ref, onMounted } from 'vue'

const loading = ref(true)  // By default, the loading value is true and passed to the loading component
onMounted(() => {
  // Change loading status after the timer simulates data loading is completed  setTimenout(() => {    
      = false    
  }, 3000)
})
</script>

At this time, we can receive the incoming value in the loading component. We can judge whether the loading component is displayed based on the value.

This is the end of this article about Vue3’s implementation of global loading instructions. For more related contents of Vue3’s global loading instructions, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!