Vue 3 自定义指令 - 元素添加 loading 效果

1,199 阅读3分钟

Vue 3 自定义指令实现 loading 加载效果

在前端开发中,有时我们需要在页面加载数据或执行特定操作时,展示一个加载动画或遮罩层来提升用户体验。在 Vue 应用中,通过自定义指令可以非常方便地实现这样的功能,同时保持代码的可维护性和可复用性。

关于 vue3 自定义指令的介绍与使用,可以先看一下 Vue 3 自定义指令简解

本demo地址:github.com/baozjj/vue-…

效果演示

20240630201720_rec_.gif 只需要在元素中使用 v-loading 自定义指令即可实现:

<template>
    <Button @click="isLoading = !isLoading"> 加载 </Button>
    当前状态: {{ isLoading }}
    <div 
      style="
          width: 200px;
          height: 100px;
          border-radius: 5px;
          border: 1px solid black;
          margin-top: 10px;
        "
      v-loading="isLoading"
    >
      我是内容
    </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
const isLoading = ref(false)
<script>

实现步骤

文件结构

在开始之前,让我们先来看一下项目的文件结构:

my-vue-directives/
│
├── src/
│   ├── directives/
│   │   ├── loading/
│   │   │   ├── index.ts     // 定义加载动画自定义指令的逻辑
│   │   │   └── style.css    // 定义加载动画的样式
│   │   ├── index.ts         // 批量注册自定义指令
│   │   └── style.css        // 公共样式
│   ├── App.vue              // 根组件
│   └── main.ts              // 入口文件,初始化Vue实例

创建自定义指令

首先,在 src/directives/loading/ 文件夹中创建一个新的文件 index.ts,用于定义加载动画自定义指令的逻辑:

import type { DirectiveBinding } from 'vue'

interface ElType extends HTMLElement {
  overlay: HTMLElement | null
}

const loadingDirective = {
  // 在元素挂载时调用
  mounted(el: ElType, binding: DirectiveBinding) {
    // 创建遮罩层
    const overlay = document.createElement('div')
    overlay.className = 'directives-loading-overlay' // 稍后在样式文件中定义
    overlay.style.display = 'none'

    // 创建 中间旋转 的元素
    const spinner = document.createElement('div')
    spinner.className = 'directives-loading-spinner' // 稍后在样式文件中定义

    overlay.appendChild(spinner)
    el.overlay = overlay
    el.style.position = 'relative' // 设置相对定位
    el.appendChild(overlay)

    // 根据元素的大小设置加载动画的尺寸
    const size = Math.min(Math.min(el.clientWidth, el.clientHeight) / 2, 30)
    spinner.style.width = `${size}px`
    spinner.style.height = `${size}px`
    spinner.style.borderWidth = `${size / 10}px`

    if (binding.value) {
      el.overlay!.style.display = 'flex'
      el.classList.add('directives-loading-active') // 稍后在样式文件中定义
    } else {
      el.overlay!.style.display = 'none'
    }
  },
  // 在绑定值更新时调用
  updated(el: ElType, binding: DirectiveBinding) {
    // 根据绑定值的变化显示或隐藏加载效果
    if (binding.value) {
      el.overlay!.style.display = 'flex'
      el.classList.add('directives-loading-active')
    } else {
      el.overlay!.style.display = 'none'
      el.classList.remove('directives-loading-active')

    }
  },
  unmounted(el: ElType) {
    // 移除加载元素
    el.removeChild(el.overlay!);
    el.overlay = null;
  }
};

export default loadingDirective;

添加样式

接下来,在 src/directives/loading/ 文件夹中创建一个 style.css 文件,用于定义遮罩层和加载动画的样式:

/* 遮罩层 */
.directives-loading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(193, 191, 191, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 999;
}
/* 旋转元素 */
.directives-loading-spinner {
  justify-content: center;
  align-items: center;
  width: 50px;
  height: 50px;
  border: 5px solid rgba(0, 0, 0, 0.5);
  border-radius: 50%;
  border-left-color: transparent;
  animation: loading-spin 1s linear infinite;
}
/* loading 状态时添加 */
.directives-loading-active {
  overflow: hidden;
  pointer-events: none; /* 禁止事件 */
}

@keyframes loading-spin {
  to {
    transform: rotate(360deg);
  }
}

批量注册自定义指令

src/directives/ 文件夹中创建一个 index.ts 文件,用于批量注册自定义指令:

import type { App, Directive } from 'vue';
import loadingDirective from './loading/index';
// 其他指令

const directives: { [key: string]: Directive } = {
  loading: loadingDirective,
  // 其他指令
};

export default {
  install(app: App) {
    Object.keys(directives).forEach(key => {
      app.directive(key, directives[key]);
    });
  }
};

并在 src/directives/ 文件夹中创建一个 style.css 文件,用于导入所有自定义指令的样式:

@import './loading/style.css';
/* 其他指令需要的样式 */

入口文件

// src/main.ts
import { createApp } from 'vue';
import App from './App.vue';
// 导入自定义指令以及样式
import directives from './directives';
import './directives/style.css'

const app = createApp(App);
app.use(directives);
app.mount('#app');

结语

通过以上步骤,就成功实现了一个可以在 Vue 3 项目中使用的简易的加载效果自定义指令。如果有任何疑问或建议,欢迎留言交流!