自定义插件之全局Loading
ElementPlus的默认全局Loading
如果完整引入了 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 |
— |
指令的方式使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <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>
|
函数式调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| <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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| import type {App, VNode,} from "vue" import {createVNode, render, cloneVNode} from "vue" import MyLoading from "@/components/MyLoading.vue"
export default { install(app: App) { const VNode: VNode = createVNode(MyLoading) render(VNode, document.body) app.config.globalProperties.$showLoading = VNode.component?.exposed.showLoading app.config.globalProperties.$hideLoading = VNode.component?.exposed.hideLoading
const weakMap = new WeakMap()
app.directive("zx-loading", { mounted(el) { if (weakMap.get(el)) return weakMap.set(el, window.getComputedStyle(el).position) }, updated(el: HTMLElement, binding: { value: Boolean }) { const oldPosition = weakMap.get(el); if (oldPosition !== 'absolute' && oldPosition !== 'relative') { el.style.position = 'relative' } 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
1 2 3 4 5 6 7 8
| import {createApp} from 'vue' import MyLoading from "@/utils/MyLoading";
const app = createApp(App)
app.use(MyLoading)
app.mount('#app')
|
使用方法一:函数式使用
调用全局方法弹出Loading
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <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>
|

使用方法二:指令式使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| <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>
|

use函数源码实现
添加 MyUse.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import type {App} from "vue" import {app} from "@/main"
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 } plugin.install(app, ...options) installList.add(plugin) }
|
使用自定义的myUse方法注册我们自定义的Loading
1 2 3 4 5 6 7 8 9 10 11 12 13
| import {createApp} from 'vue'
import MyLoading from "@/utils/MyLoading";
import myUse from "@/utils/MyUse";
export const app = createApp(App)
myUse(MyLoading)
app.mount('#app')
|