赞
踩
如果完整引入了 Element Plus,那么 app.config.globalProperties
上会有一个全局方法$loading
,同样会返回一个 Loading 实例。
名称 | 说明 | 类型 | 默认 |
---|---|---|---|
target | Loading 需要覆盖的 DOM 节点。 可传入一个 DOM 对象或字符串; 若传入字符串,则会将其作为参数传入 document.querySelector 以获取到对应 DOM 节点 | string / HTMLElement | document.body |
body | 同 v-loading 指令中的 body 修饰符 | boolean | false |
fullscreen | 同 v-loading 指令中的 fullscreen 修饰符 | boolean | true |
lock | 同 v-loading 指令中的 lock 修饰符 | boolean | false |
text | 显示在加载图标下方的加载文案 | string | — |
spinner | 自定义加载图标类名 | string | — |
background | 遮罩背景色 | string | — |
customClass | Loading 的自定义类名 | 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>
我们自己动手来实现一个和ElementPlus一样的Loading,同时支持函数调用和指令调用
<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>
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 }) } } }) } }
在上面的文件中定义了两个全局函数和一个自定义指令
在 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>
<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>
添加 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')
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。