vue3自定义指令实现一个v-loading

1,381 阅读1分钟

背景

用过element v-loading指令的同学,相信都是对其很赞的,但有时在我们开发移动端或者选则在element之外的其他UI框架的时候,其实并没有集成v-loading这个指令,大多就一个loading图标之类的,使用的时候需要在template上面去加各种判断来显示,非常不爽。so,这必须得来实现一个v-loading指令,让代码撸起来更舒服。

实现步骤

  • 创建一个loading.vue组件,包含旋转动画,和加载文字
  • 创建loadingDirective
    • binding.value值为true的时候,将loading组件append到目标dom上
    • binding.value值为false的时候,将loading组件remove掉

上代码

文件目录

image.png

loading.vue组件

<template>
  <div class="loading">
    <img src="@/assets/loading.svg" class="loading-icon" alt="">
    <span>加载中...</span>
  </div>
</template>

<style scoped>
@keyframes rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

.loading{
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    background-color: rgba(255, 255, 255, .8);
    color: #333;
}
.loading-icon{
    width: 30px;
    height: 30px;
    animation: rotate 1s linear infinite;
}
</style>

loading.directive.ts

import Loading from "@/components/Loading.vue";
import { createApp } from "vue";

// loading组件的实例,mounted的时候保存,updated的时候使用
let loadingDom: any; 

// 保存dom的初始position属性,卸载loading效果时还原
let elPosition: string; 

export const loading = {
  mounted(el, binding) {
    init(el);
    if (binding.value === true) {
        addLoading(el)
    } else {
        removeLoading(el)
    }
  },
  updated(el, binding) {
    if (binding.value === true) {
        addLoading(el)
    } else {
        removeLoading(el)
    }
  },
};

/**
 * 组件挂载时初始化
 * 
 * @param el 
 */
const init = (el) => {
    const app = createApp(Loading)
    loadingDom = app.mount(document.createElement('div'))

    elPosition = window.getComputedStyle(el).position
}

/**
 * 添加loading效果
 * 
 * @param el 
 */
const addLoading = (el) => {
    removeLoading(el)
    el.style.position = 'relative'
    el.appendChild(loadingDom.$el)
}

/**
 * 取消loading效果
 * 
 * @param el 
 */
const removeLoading = (el) => {
    el.style.position = elPosition || 'static'
    if (el.contains(loadingDom.$el)) {
        el.removeChild(loadingDom.$el)
    }
}

loading指令全局引入

  • /directive/index.ts
import type { App } from "vue";

import { loading } from "./loading.directive.ts";

export function setupDirective(app: App<Element>) {
  app.directive("custom-loading", loading);
}

验证

  • app.vue

image.png

image.png

image.png

image.png