前言
在现代Web开发中,提升用户体验一直是开发者们追求的目标之一。其中,一个常见的场景就是在用户与应用程序进行交互时,特别是当进行异步操作时(如网络请求),为用户提供即时的反馈,避免用户因为等待而感到困惑或不满。这通常通过显示一个加载指示器(通常称为Loading效果)来实现。本文将深入探讨如何在Vue 3中通过自定义指令的方式来实现Loading加载效果。自定义指令是Vue提供的一种强大工具,允许我们在Vue模板中附加自定义行为。通过自定义指令,我们可以轻松地创建可复用的、可配置的加载效果组件,并将其应用于任何需要显示加载状态的元素上。
演示效果图
新建index.vue文件
在components
目录下新建loading
目录,并在loading
目录下新建index.vue
文件
<template>
<div class="loading-container" v-if="show">
<div class="loader"></div>
<div class="tips">正在快马加鞭的加载中....</div>
</div>
</template>
<script setup name="Loading">
const show = ref(false);
const showLoading = () => {
show.value = true;
document.body.style.overflow = "hidden";
document.addEventListener("touchmove", () => {}, true);
};
const hideLoading = () => {
show.value = false;
var mo = function (e) {
e.preventDefault();
};
document.body.style.overflow = "";
document.removeEventListener("touchmove", mo, true);
};
onMounted(() => {});
defineExpose({
show,
showLoading,
hideLoading,
});
</script>
<style scoped>
.loading-container {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 9999;
background-color: rgba(255, 255, 255, .9);
}
.tips {
font-family: "Open Sans";
color: #52b852;
font-size: 1rem;
width: 100%;
text-align: center;
position: absolute;
top: 55%;
line-height: 30px;
}
.loader {
width: 40px;
aspect-ratio: 0.577;
clip-path: polygon(0 0, 100% 100%, 0 100%, 100% 0);
position: relative;
animation: l19 2s infinite linear;
overflow: hidden;
position: relative;
left: 50%;
top: 45%;
margin: 0 0 0 -25px;
}
.loader:before {
content: "";
position: absolute;
inset: -150% -150%;
background: repeating-conic-gradient(
from 30deg,
#ffabab 0 60deg,
#abe4ff 0 120deg,
#ff7373 0 180deg
);
animation: inherit;
animation-direction: reverse;
}
@keyframes l19 {
100% {
transform: rotate(360deg);
}
}
</style>
新建loading.js文件
在index.vue
的同级目录中新建loading.js
文件来创建自定义指令
import {createVNode, render, cloneVNode} from "vue"
import Loading from "./index.vue"
export default {
install(app) {
// 使用vue底层的createVNode方法将组件渲染为虚拟节点
const VNode = createVNode(Loading)
// 使用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("sy-loading", {
mounted(el) {
if (weakMap.get(el)) return
// 记录当前绑定元素的position
weakMap.set(el, window.getComputedStyle(el).position)
},
updated(el, binding) {
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
})
}
}
})
}
}
1. loading.ts
TS写法。上面是js写法,选其中一种即可
import type {App, VNode,} from "vue"
import {createVNode, render, cloneVNode} from "vue"
import Loading from "./index.vue"
export default {
install(app: App) {
// 使用vue底层的createVNode方法将组件渲染为虚拟节点
const VNode: VNode = createVNode(Loading )
// 使用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("sy-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.js引入
在main.js
中引入loading.js文件。
import Loading from '@/components/loading/Loading.js'
app.use(Loading)
在组件中使用
v-sy-loading="fullscreenLoading"
在任意组件中的任意标签元素中添加v-sy-loading指定,并设置一个boolean值的参数,即可控制页面的loading加载效果
End
通过本文的介绍,我们详细探讨了如何在Vue 3中利用自定义指令来实现灵活且可复用的Loading加载效果。这一功能不仅优化了用户与应用程序之间的交互体验,还使得加载状态的显示更加直观和易于管理。我们介绍了自定义指令的基本概念、创建过程以及如何在Vue模板中优雅地应用该指令。希望这些内容能帮助你在Vue 3项目中更好地实现Loading加载效果,提升用户体验。未来,随着Vue.js的不断发展和完善,我们期待有更多创新的方法来优化用户界面的交互效果。