前言

  • 公司在开发组件库的时候,我在封装loading这个组件时,需要⼀个全局使⽤的loading,⽤插件也可以搞定,但是项⽬开发周期不太紧张,所以决定⾃⼰手写⼀个。
  • 写⼀个全局的 componnets 也⾏,但是需要在每个使⽤的页⾯进⾏导⼊并且注册,因为 loading ⽤到的地⽅会很多,所以我觉得需要用更优雅的方式来实现它。

1.完成loading组件

<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('') // 加载提示文字
 // 更改加载提示文字
 const setTitle = (val) => {
    title.value = val
}
    
 // 暴露出去
 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.新建⼀个loading.js⽂件,⽤来写loading的⾃定义指令逻辑

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

在创建的 loadingDirective 对象中,写⼊两个钩⼦函数,因为我们希望在这两个生命周期函数,对它进⾏操作。

2.1 创建这个组件对应的 DOM

  • 新建⼀个新的vue实例,再对它进⾏⼀个动态的挂载,挂载之后我们就可以拿到这个 DOM 的实例了

具体实现看如下代码块

import { createApp } from 'vue' // 导⼊ createApp ⽅法
import Loading from './loading' // 导⼊我们写好的 loading 组件
const loadingDirective = {
    mounted(el, binding) {
        // 创建app对象跟组件为我们写好的 loading 组件
        const app = createApp(Loading)
        // 动态创建⼀个div节点,将app挂载在div上
        // 我们的 loading 组件将替换此 div 标签的 innerHTML
        const instance = app.mount(document.createElement('div'))
    },
    updated(el, binding) {},
}
return loadingDirective  // 导出

2.2 在 loading.js 文件中新增两个方法,分别是插入节点和移除节点。

import { createApp } from 'vue' // 导⼊ createApp ⽅法
import Loading from './loading' // 导⼊我们写好的 loading 组件
const loadingDirective = {
    mounted(el, binding) {
        // 创建app对象跟组件为我们写好的 loading 组件
        const app = createApp(Loading)
        
        // 动态创建⼀个div节点,将app挂载在div上
        // 我们的 loading 组件将替换此 div 标签的 innerHTML
        const instance = app.mount(document.createElement('div'))
        
        // 因为在updated也需要⽤到 instance 所以将 instance 添加在 el 上
        // 在 updated中 通过el.instance 可访问到
        el.instance = instance
        
        // v-loading传过来的值储存在 binding.value 中
        if (binding.value) {
            append(el)
        }
    },
    updated(el, binding) {
       remove(el)
    },
}
return loadingDirective  // 导出

// 插入节点
function append(el) {
    // 向el节点插⼊动态创建的 div 节点,内容就是我们的 loading 组件
    el.appendChild(el.instance.$el)  
}
// 移除节点
function remove(el) {
    // 移除动态创建的 div 节点
    el.removeChild(el.instance.$el)
}

2.3 完善 updated 周期函数

在⼀开始的时候将节点插⼊,在 v-loading 的值发⽣改变时候触发 updated 我们将节点移除, 但是这样写是不合适的,我们需要完善⼀下在 updated 中的操作。

updated (el, binding) {
    // 如果value的值有改变,那么我们去判断进⾏操作
    if (binding.value !== binding.oldValue) {
      // 三元表达式 true 插入 false 移除
      binding.value ? append(el) : remove(el)
    }

基本的指令已经完成的差不多了。

2.4 解决存在的三个问题

  • 位置定位的问题,如果所在挂载的 DOM 元素有定位属性,那么没有问题,但是所在挂载的 DOM 元素没有定位的时候,那么它⾃⾝的绝对定位,就有可能出现问题。
  • 页面滚动的问题,如果出现 loading 效果, 遮罩层后面的页面会发生滚动,滚动遮罩层时会造成底部页面跟着一块滚动。
  • 加载文字需求,我们还可以加入⼀段⽂字进⾏展⽰,不同的地⽅需要等待时的⽂字会有不同。

2.4.1 ⾸先来解决第⼀个和第二个问题

  • 在插⼊节点的时候去判断挂载的节点有没有定位,如果没有则为其添加相对定位。
  • 在插⼊节点的时候为其添加禁止/隐藏滚动。
  • 在移除节点的时候移除类名。
const relative = 'g-relative' // g-relative 全局相对定位样式名称
const hidden = 'g-hidden' // g-hidden 全局禁止/隐藏滚动样式名称
// 插入节点
function append(el) {
const style = getComputedStyle(el)
    el.classList.add(hidden) // 添加类名
  if (['absolute', 'relative', 'fixed'].indexOf(style.position) === -1) {
    el.classList.add(relative) // 添加类名
  }
    // 向el节点插⼊动态创建的 div 节点,内容就是我们的 loading 组件
    el.appendChild(el.instance.$el)
}
// 移除节点
function remove(el) {
    // 移除动态创建的 div 节点
    el.removeChild(el.instance.$el)
    el.classList.remove(relative) // 移除类名
    el.classList.remove(hidden) // 移除类名
}

g-relative g-hidden 是我在全局css⽂件中写的样式

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

2.4.2 最后解决第三个问题,动态显示加载文字。

在⽤的地⽅使⽤ :[] 的语法

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

const title = ref('拼命加载中...')
const loading = ref(true)
}
</script>
  • 传⼊title之后需要进⾏接收,并插⼊titleloading
  • 通过binding.arg可接收到 :[] 传来的值
import { createApp } from 'vue' // 导⼊ createApp ⽅法
import Loading from './loading' // 导⼊我们写好的 loading 组件
const loadingDirective = {
    mounted(el, binding) {
        // 创建app对象跟组件为我们写好的 loading 组件
        const app = createApp(Loading)
        
        // 动态创建⼀个div节点,将app挂载在div上
        // 我们的 loading 组件将替换此 div 标签的 innerHTML
        const instance = app.mount(document.createElement('div'))
        
        // 因为在updated也需要⽤到 instance 所以将 instance 添加在 el 上
        // 在 updated中 通过el.instance 可访问到
        el.instance = instance
        
        // v-loading传过来的值储存在 binding.value 中
        if (binding.value) {
            append(el)
        }
        
        // 在此判断是否有title值
        if (binding.arg !== 'undefined') {
         // setTitle 使我们在loading组件中定义的⽅法
          el.instance.setTitle(binding.arg)  
        }
    },
    updated (el, binding) {
        // 在此判断是否有title值
        if (binding.arg !== 'undefined') {
         // setTitle 使我们在loading组件中定义的⽅法
          el.instance.setTitle(binding.arg)  
        }
        
        // 如果value的值有改变,那么我们去判断进⾏操作
        if (binding.value !== binding.oldValue) {
          // 三元表达式 true 插入 false 移除
          binding.value ? append(el) : remove(el)
        }
     }
}
return loadingDirective  // 导出

const relative = 'g-relative' // g-relative 全局相对定位样式名称
const hidden = 'g-hidden' // g-hidden 全局禁止/隐藏滚动样式名称
// 插入节点
function append(el) {
const style = getComputedStyle(el)
    el.classList.add(hidden) // 添加类名
  if (['absolute', 'relative', 'fixed'].indexOf(style.position) === -1) {
    el.classList.add(relative) // 添加类名
  }
    // 向el节点插⼊动态创建的 div 节点,内容就是我们的 loading 组件
    el.appendChild(el.instance.$el)
}
// 移除节点
function remove(el) {
    // 移除动态创建的 div 节点
    el.removeChild(el.instance.$el)
    el.classList.remove(relative) // 移除类名
    el.classList.remove(hidden) // 移除类名
}

3.在main.js文件中引⼊注册

想要在全局使⽤这个自定义指令,那么我们需要在 main.js 文件中去引⼊注册。

import { createApp } from 'vue'
import App from './App.vue'
import '@/assets/scss/global.css' // 引入全局样式文件
import loadingDirective from '@/views/loading/directive' //引⼊loading⾃定义指令
createApp(App)
  .directive('loading', loadingDirective)  // 全局注册loading指令
  .mount('#app')

4.在页面中使用自定义loading指令

onMounted 的时候,判断loading 是否显⽰,在做判断之前,先来看看我们在写好 loading ⾃定义指令之后,该如何在页面中使用它。

<template>
  <div v-loading="loading"></div> // 只需在节点上写上 v-loading='loading' 即可,后边的loading是⼀个值
</template>
<script setup>
import { ref, onMounted } from 'vue'

const loading = ref(true)  // 默认让 loading 值为true 传给loading组件

onMounted(() => {
  // 定时器模拟数据加载完成之后更改 loading 状态
  setTimenout(() => {    
     loading.value = false    
  }, 3000)
})
</script>

此时我们在 loading 组件中就可以接收到传⼊的值,我们根据值来判断是否显⽰ loading 组件

到此这篇关于Vue3实现全局loading指令的文章就介绍到这了,更多相关Vue3 全局loading指令内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Vue3实现全局loading指令的示例详解的更多相关文章

  1. Swift 4.0 MapView运行缓慢

    自从我更新到Xcode9和Swift4.0后,我的模拟器中的mapView运行速度非常慢.我认为这是因为代码会被弃用或因为我的代码很广泛而导致速度变慢,但是我将mapView添加到空白项目中,并且mapView导航/缩放的速度也很慢!我需要配置一些设置来在模拟器中修复此问题吗?等待修复编辑一条评论指出,这已在iOS11.3测试版中得到修复.

  2. vue自定义加载指令v-loading占位图指令v-showimg

    这篇文章主要为大家介绍了vue自定义加载指令和v-loading占位图指令v-showimg的示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  3. vue3获取当前路由地址

    本文详细讲解了vue3获取当前路由地址的方法,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  4. 十分钟带你快速上手Vue3过渡动画

    在开发中我们想要给一个组件的显示和消失添加某种过渡动画,可以很好的增加用户体验,下面这篇文章主要给大家介绍了关于如何快速上手Vue3过渡动画的相关资料,需要的朋友可以参考下

  5. 用vue3封装一个符合思维且简单实用的弹出层

    最近新项目中需要一个弹窗组件,所以我就做了一个,下面这篇文章主要给大家介绍了关于如何利用vue3封装一个符合思维且简单实用的弹出层,文中通过实例代码介绍的非常详细,需要的朋友可以参考下

  6. 使用Vite+Vue3+Vant全家桶快速构建项目步骤详解

    这篇文章主要为大家介绍了使用Vite+Vue3+Vant全家桶快速构建项目步骤详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  7. vue3中$attrs的变化与inheritAttrs的使用详解

    $attrs现在包括class和style属性。 也就是说在vue3中$listeners不存在了,vue2中$listeners是单独存在的,在vue3 $attrs包括class和style属性, vue2中 $attrs 不包含class和style属性,这篇文章主要介绍了vue3中$attrs的变化与inheritAttrs的使用 ,需要的朋友可以参考下

  8. jsp的常用指令有哪些(编译指令/动作指令整理)

    jsp的常用指令有哪些?Jsp包含三个编译指令和七个动作指令,具体祥看下文,希望对你面试有所帮助

  9. vue3中proxy的基本用法说明

    这篇文章主要介绍了vue3中proxy的基本用法说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  10. vue的指令和插值问题汇总

    Vue 是一套用于构建用户界面的渐进式框架,Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合,这篇文章主要介绍了vue的指令和插值总结,需要的朋友可以参考下

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部