Preface
- When the company was developing component libraries, I was encapsulating
loading
This 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 global
componnets
It's OK, but it needs to be imported and registered on each page used, becauseloading
There 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
CreatedloadingDirective
In 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 this
DOM
An 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 mounted
DOM
The element has positioning attributes, so there is no problem, but it is mountedDOM
When an element is not positioned, then its own absolute positioning may cause problems. - Page scrolling problem, if
loading
Effect: 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-hidden
It'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>
- Incoming
title
After that, you need to receive and insert ittitle
existloading
middle - pass
Can 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 to
Introduce 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
exist
onMounted
When judgingloading
Whether it is displayed, before making a judgment, let’s see if we are writingloading
After 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!