Vue3实现全局loading指令的示例详解

目录
  • 前言
  • 1.完成loading组件
  • 2.新建⼀个loading.js⽂件,⽤来写loading的⾃定义指令逻辑
    • 2.1 创建这个组件对应的 DOM
    • 2.2 在 loading.js 文件中新增两个方法,分别是插入节点和移除节点。
    • 2.3 完善 updated 周期函数
    • 2.4 解决存在的三个问题
      • 2.4.1 ⾸先来解决第⼀个和第二个问题
      • 2.4.2 最后解决第三个问题,动态显示加载文字。
  • 3.在main.js文件中引⼊注册
  • 4.在页面中使用自定义loading指令

前言

  • 公司在开发组件库的时候,我在封装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指令内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 基于vue+element实现全局loading过程详解

    项目中使用的是vue+element实现的全局loading 1.引入所需组件,这里主要就是router和element组件,element组件引入可以参考element官网 2.下面就是重点及代码实现了 首先是全局的一个变量配置参数,代码如下: //全局页面跳转是否启用loading export const routerLoading = true; //全局api接口调用是否启用loading export const apiLoading = true; //loading参数配置 ex

  • vue 全局封装loading加载教程(全局监听)

    前言: 为了页面美观,请求接口的时候延迟没有数据,页面感觉狠卡顿,封装loading,请求接口成功后隐藏掉(我这是用的vant 组件根据自己情况进行改变). 第一步: 建立loading.vue <template> <div class="loading"> <van-loading size="36px" vertical>加载中...</van-loading> </div> </templat

  • vue3.0自定义指令(drectives)知识点总结

    在大多数情况下,你都可以操作数据来修改视图,或者反之.但是还是避免不了偶尔要操作原生 DOM,这时候,你就能用到自定义指令. 举个例子,你想让页面的文本框自动聚焦,在没有学习自定义指令的时候,我们可能会这么做. const app = Vue.createApp({ mounted(){ this.$refs.input.focus(); }, template: `<input type="text" ref="input" />`, }); 在mou

  • Vue自定义全局Toast和Loading的实例详解

    如果我们的Vue项目中没有用到任何UI框架的话,为了更好的用户体验,肯定会用到loading和toast.那么我们就自定义这两个组件吧. 1.Toast组件 首先,在common下新建global文件夹,存放我们的toast.vue和toast.js两个文件(当然文件的具体位置你可以自行安排). (1). toast.vue <template lang="html"> <div v-if="isShowToast" class="toa

  • 如何写一个 Vue3 的自定义指令

    目录 背景 插件 指令的实现 前端巅峰 以下文章来源于微信公众号前端巅峰 背景 众所周知,Vue.js 的核心思想是数据驱动 + 组件化,通常我们开发页面的过程就是在编写一些组件,并且通过修改数据的方式来驱动组件的重新渲染.在这个过程中,我们不需要去手动操作 DOM. 然而在有些场景下,我们还是避免不了要操作 DOM.由于 Vue.js 框架接管了 DOM 元素的创建和更新的过程,因此它可以在 DOM 元素的生命周期内注入用户的代码,于是 Vue.js 设计并提供了自定义指令,允许用户进行一些底

  • vue3 自定义指令详情

    目录 一.注册自定义指令 1.1.全局自定义指令 1.2.局部自定义指令 二.自定义指令中的生命周期钩子函数 三.自定义指令钩子函数的参数 四.自定义指令参数 一.注册自定义指令 以下实例都是实现一个输入框自动获取焦点的自定义指令. 1.1.全局自定义指令 在vue2中,全局自定义指令通过 directive 挂载到 Vue 对象上,使用 Vue.directive('name',opt). 实例1:Vue2 全局自定义指令 Vue.directive('focus',{ inserted:(e

  • 基于Vue 实现一个中规中矩loading组件

    前言 最近有一个新的项目,UI大佬不知道从哪里找来了一张GIF丢到蓝湖,说作为全局的页面loading ,但是自己想了想,还是选择画一个. 一开始想过用svg,canvas;最终还是选择了css3+js来实现这个效果: gif的缺点挺多,至于为什么又排除了svg和canvas: 是因为css3+js可控性更强,不管是大小还是颜色,还是响应式(我的项目走的vh,vw)那套来适配: 可以借助打包插件,达到loading的大小适配; 效果 UI大佬提供的GIF 实现的效果[在线codesandbox预

  • Vue3实现全局loading指令的示例详解

    目录 前言 1.完成loading组件 2.新建⼀个loading.js⽂件,⽤来写loading的⾃定义指令逻辑 2.1 创建这个组件对应的 DOM 2.2 在 loading.js 文件中新增两个方法,分别是插入节点和移除节点. 2.3 完善 updated 周期函数 2.4 解决存在的三个问题 2.4.1 ⾸先来解决第⼀个和第二个问题 2.4.2 最后解决第三个问题,动态显示加载文字. 3.在main.js文件中引⼊注册 4.在页面中使用自定义loading指令 前言 公司在开发组件库的时

  • vue.js内部自定义指令与全局自定义指令的实现详解(利用directive)

    前言 大家都知道在Vue中,我们平时数据驱动视图时候,内部自带的指令有时候解决不了一些需求,这时候,Vue给我们一个很好用的东东来实现自定义指令,这就是directive.下面话不多说了,来一起看看详细的介绍: directive 这个单词是我们写自定义指令的关键字哦 自定义指令为我们提供了几个钩子函数,这时候你一定好奇什么是钩子函数,说简单点,就是集中表现状态 bind: 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作. inserted: 被绑

  • vue3.0中友好使用antdv示例详解

    前言 随着我们vue3.0的出现,我们的ui组件库也有了一些变化,像我们的旧版的element-ui已经不能在vue3.0中使用了,如果要使用element的话需要使用最新版的element-plus,由于发现它并不太好用,因此我选择了Ant Design Vue. 如果我们以前经常使用antd的话,我们使用起来这个上手会非常方便. 在vue3.0中引入我们的antdv 1.首先使用我们的vue/cli创建vue3.0项目并使用less 2. 在vue3.0中使用的话我们需要安装 ant-des

  • Vue3中如何使用异步请求示例详解

    目录 1.前言 2.快速开始 2.1.思路 2.2.安装&封装axios 2.3.设计接口 2.4.设计视图 2.5.最终效果 总结 1.前言 接上节,我们初步体验了layui-vue的用法.相比其他ui框架,layui-vue的数据结构显得不是非常友好,但是经过数据拼凑也是能够成功运行的. 今天我们就主要介绍下在实际开发中最常用到的前后端接口交互.因为大多数时候前端为了高性能,对于后端接口的调用都会采用异步的方式.那该如何在vue3中使用异步请求渲染页面呢? 2.快速开始 2.1.思路 预期:

  • vue自定义指令directive实例详解

    下面给大家介绍vue自定义指令directive,具体内容如下所示: 官网截图实例 vue除了一些核心的内部定义的指令(v-model,v-if,v-for,v-show)外,vue也允许用户注册自己的一些功能性的指令,有时候你实在是要对Dom操作,这个时候是自定义指令最合适的了. 来直接看例子:当页面加载时使得元素获得焦点(autofocus 在移动版 Safari 是不支持的),就是当页面加载好了,不做任何的操作使得表单自动获得焦点,光标自动在某个表单上代码如下: Vue.directive

  • vue3使用自定义指令实现el dialog拖拽功能示例详解

    目录 实现el-dialog的拖拽功能 通过自定义指令实现拖拽功能 实现拖拽功能 使用方式 实现el-dialog的拖拽功能 这里指的是 element-plus 的el-dialog组件,一开始该组件并没有实现拖拽的功能,当然现在可以通过设置属性的方式实现拖拽. 自带的拖拽功能非常严谨,拖拽时判断是否拖拽出窗口,如果出去了会阻止拖拽. 如果自带的拖拽功能可以满足需求的话,可以跳过本文. 通过自定义指令实现拖拽功能 因为要自己操作dom(设置事件),所以感觉还是使用自定义指令更直接一些,而且对原

  • vue3自定义插件的作用场景及使用示例详解

    目录 插件的作用场景 插件的定义(注册) 插件的安装 插件的使用 插件中的Provide/inject 插件的作用场景 在vue2的插件那篇文章我们介绍过插件其实就是vue的增强功能.通常来为vue添加全局功能的.在vue3中插件的功能也是一样的,只是它们在定义上有所不同. 通过app.component()和app.directive()注册一到多个全局组件或自定义指令 通过app.provide()使一个资源可被注入进整个应用 向app.config.globalProperties中添加一

  • Vue3中Vuex状态管理学习实战示例详解

    目录 引言 一.目录结构 二.版本依赖 三.配置Vuex 四.使用Vuex 引言 Vuex 是 Vue 全家桶重要组成之一,专为 Vue.js 应用程序开发的 状态管理模式 + 库 ,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 一.目录结构 demo/ package.json vite.config.js index.html public/ src/ api/ assets/ common/ components/ store/ index.

  • vue+axios+element ui 实现全局loading加载示例

    实现全局loading加载 分析需求,我们只需要在请求发起的时候开始loading,响应结束的时候关闭loading,就这么简单 对不对? import axios from 'axios'; import { Message, Loading } from 'element-ui'; import Cookies from 'js-cookie'; import router from '@/router/index' let loading //定义loading变量 function st

随机推荐