vue3中通过自定义指令实现loading加载效果

前言

在现代Web开发中,提升用户体验一直是开发者们追求的目标之一。其中,一个常见的场景就是在用户与应用程序进行交互时,特别是当进行异步操作时(如网络请求),为用户提供即时的反馈,避免用户因为等待而感到困惑或不满。这通常通过显示一个加载指示器(通常称为Loading效果)来实现。本文将深入探讨如何在Vue 3中通过自定义指令的方式来实现Loading加载效果。自定义指令是Vue提供的一种强大工具,允许我们在Vue模板中附加自定义行为。通过自定义指令,我们可以轻松地创建可复用的、可配置的加载效果组件,并将其应用于任何需要显示加载状态的元素上。

演示效果图

image.png

新建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的不断发展和完善,我们期待有更多创新的方法来优化用户界面的交互效果。

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值