vue3自定义全局Loading

自定义插件之全局Loading

ElementPlus的默认全局Loading

如果完整引入了 Element Plus,那么 app.config.globalProperties 上会有一个全局方法$loading,同样会返回一个 Loading 实例。

名称说明类型默认
targetLoading 需要覆盖的 DOM 节点。 可传入一个 DOM 对象或字符串; 若传入字符串,则会将其作为参数传入 document.querySelector以获取到对应 DOM 节点string / HTMLElementdocument.body
bodyv-loading 指令中的 body 修饰符booleanfalse
fullscreenv-loading 指令中的 fullscreen 修饰符booleantrue
lockv-loading 指令中的 lock 修饰符booleanfalse
text显示在加载图标下方的加载文案string
spinner自定义加载图标类名string
background遮罩背景色string
customClassLoading 的自定义类名string

指令的方式使用

<template>
   <div class="box" v-loading="isLoading">
      content
   </div>
   <el-button type="primary" @click="showDivLoading">显示loading</el-button>
</template>

<script setup lang="ts">
// 显示局部loading
let isLoading = ref(false)

const showDivLoading = () => {
  isLoading.value = !isLoading.value
}

</script>

<style scoped>
.box {
  width: 200px;
  height: 200px;
  border: 1px solid;
}
</style>

函数式调用

<template>
  <el-button type="primary" @click="showLoading">showLoading</el-button>
</template>

<script setup lang="ts">
import {getCurrentInstance} from 'vue'
// 获取当前实例
const {proxy} = getCurrentInstance()

// 显示全局loading
const showLoading = () => {
  const loading = proxy.$loading()
  setTimeout(() => {
    loading.close()
  }, 2000)
}
</script>

自定义全局Loading

我们自己动手来实现一个和ElementPlus一样的Loading,同时支持函数调用和指令调用

添加MyLoading.vue

<template>
  <transition enter-active-class="animate__animated animate__fadeIn"
              leave-active-class="animate__animated animate__fadeOut">
    <div class="root-box" v-if="show">
      <div class="wrap">
        <img src="../assets/images/loading.gif"/>
      </div>
    </div>
  </transition>
</template>

<script setup>
let show = ref(false)

const showLoading = () => {
  show.value = true
}
const hideLoading = (callback) => {
  show.value = false
  callback && setTimeout(() => callback(), 500)
}

defineExpose({
  show,
  showLoading,
  hideLoading
})

</script>

<style scoped lang="scss">
.animate__animated.animate__fadeIn {
  --animate-duration: 0.5s;
}

.animate__animated.animate__fadeOut {
  --animate-duration: 0.5s;
}

.root-box {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  right: 0;
  margin: 0;
  background-color: rgba(255, 255, 255, 0.9);
  z-index: 2000;
  display: flex;
  justify-content: center;
  align-items: center;

  .wrap {
    width: 100px;
    height: 100px;
    display: flex;
    justify-content: center;
    align-items: center;
    overflow: hidden;

    img {
      width: 100%;
      transform: scale(2.5);
    }
  }
}
</style>

添加MyLoading.ts

import type {App, VNode,} from "vue"
import {createVNode, render, cloneVNode} from "vue"
import MyLoading from "@/components/MyLoading.vue"

export default {
    install(app: App) {
        // 使用vue底层的createVNode方法将组件渲染为虚拟节点
        const VNode: VNode = createVNode(MyLoading)
        // 使用render函数将组件挂载到body中
        render(VNode, document.body)
        // 定义全局方法设置组件的显示和隐藏
        app.config.globalProperties.$showLoading = VNode.component?.exposed.showLoading
        app.config.globalProperties.$hideLoading = VNode.component?.exposed.hideLoading

        const weakMap = new WeakMap()

        // 自定义Loading指令
        app.directive("zx-loading", {
            mounted(el) {
                if (weakMap.get(el)) return
                //  记录当前绑定元素的position
                weakMap.set(el, window.getComputedStyle(el).position)
            },
            updated(el: HTMLElement, binding: { value: Boolean }) {
                const oldPosition = weakMap.get(el);
                // 如果不是position: relative或者absolute,就设置为relative
                // 这里的目的是确保loading组件正确覆盖当前绑定的元素
                if (oldPosition !== 'absolute' && oldPosition !== 'relative') {
                    el.style.position = 'relative'
                }
                // 克隆一份loading元素,
                // 作用是当页面上有多个zx-loading时,每个dom都维护一份属于自己的loading,不会冲突
                const newVNode = cloneVNode(VNode)
                // 挂载当前节点
                render(newVNode, el)
                // 判断绑定的值
                if (binding.value) {
                    newVNode.component?.exposed.showLoading()
                } else {
                    newVNode.component?.exposed.hideLoading(() => {
                        // 还原布局方式
                        el.style.position = oldPosition
                    })
                }
            }
        })
    }
}

在上面的文件中定义了两个全局函数和一个自定义指令

  • $showLoading:全局显示一个Loading
  • $hideLoading:关闭全局的Loading
  • zx-loading:自定义指令

在main.ts中挂载

main.ts 中去挂载我们自定义的 Loading

import {createApp} from 'vue'
import MyLoading from "@/utils/MyLoading";

const app = createApp(App)
// 引入自定义的全局Loading
app.use(MyLoading)

app.mount('#app')

使用方法一:函数式使用

调用全局方法弹出Loading

<template>
    <!--自定义全局loading-->
    <el-button type="primary" @click="showMyLoading">显示自定义的全局loading</el-button>
</template>

<script setup lang="ts">
import {getCurrentInstance} from 'vue'
// 获取当前实例
const {proxy} = getCurrentInstance()

// 全局显示自定义loading
const showMyLoading = () => {
  proxy.$showLoading()
  setTimeout(() => {
    proxy.$hideLoading()
  }, 2000)
}
</script>

image-20230925171920861

使用方法二:指令式使用

<template>
  <div>
    <!--自定义的loading指令使用-->
    <div class="box" v-zx-loading="isLoading">
      指令的方式使用
    </div>
    <el-button type="primary" @click="showDivLoading">显示loading</el-button>

	<!--自定义的loading指令使用-->      
    <div class="parent">
      <div class="child" v-zx-loading="childLoading">
      </div>
    </div>
    <el-button type="primary" @click="showChildLoading">显示childLoading</el-button>
  </div>
</template>

<script setup lang="ts">
// 显示局部loading
let isLoading = ref(false)
const showDivLoading = () => {
  isLoading.value = !isLoading.value
}

const childLoading = ref(false)
const showChildLoading = () => {
  childLoading.value = !childLoading.value
}
</script>

<style scoped lang="scss">
.box {
  width: 200px;
  height: 200px;
  border: 1px solid;
}

.parent {
  position: relative;
  width: 300px;
  height: 300px;
  border: 1px solid;
  padding: 30px;

  .child {
    position: absolute;
    right: 30px;
    bottom: 30px;
    width: 200px;
    height: 200px;
    border: 1px solid;
  }
}
</style>

image-20230925172100385

use函数源码实现

添加 MyUse.ts

import type {App} from "vue"
import {app} from "@/main"

// 定义一个接口,声明install方法必传
interface Use {
    install: (app: App, ...options: any[]) => void
}

const installList = new Set()

export default function myUse<T extends Use>(plugin: T, ...options: any[]) {
    // 判断这个插件是否已经注册过了,如果注册过了则报错
    if (installList.has(plugin)) {
        console.error("Plugin already installed")
        return
    }
    // 调用插件身上的install方法,并传入main.ts导出的app
    plugin.install(app, ...options)
    installList.add(plugin)
}

使用自定义的myUse方法注册我们自定义的Loading

import {createApp} from 'vue'

// 自定义全局Loading
import MyLoading from "@/utils/MyLoading";
// 自定义app.use方法
import myUse from "@/utils/MyUse";


export const app = createApp(App)
// 引入自定义的全局Loading
myUse(MyLoading)

app.mount('#app')
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue3中,自定义指令可以传递参数。首先,在全局中使用`Vue.directive`方法来定义指令,该方法的第一个参数是指令的名字,第二个参数是一个对象,包含了指令的各种属性和方法。其中,属性中的`bind`函数可以接收一个`binding`参数,它包含了一些信息,其中的`value`属性就是传入的参数值。比如,我们可以定义一个名为`myDirective`的指令,然后在组件中使用该指令,并传入参数`loading`,如下所示: ```javascript Vue.directive('myDirective', { bind(el, binding) { // binding.value就是传入的参数值 console.log(binding.value); // 输出:loading } }) // 在组件中使用指令 <template> <div v-myDirective="loading"></div> </template> export default { data() { return { loading: 'loading' } } } ``` 在上述代码中,`myDirective`指令绑定到`<div>`元素上,通过`v-myDirective`来使用,并将参数`loading`传入。在指令的`bind`函数中,我们可以通过`binding.value`来获取到传入的参数值。 这样,我们就可以在Vue3中使用自定义指令并传递参数了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [vue自定义指令实现方法详解](https://download.csdn.net/download/weixin_38592502/14902824)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Vue3 自定义指令详解](https://blog.csdn.net/weixin_46831501/article/details/124167378)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值