教你用vue自定义指令做一个组件的遮罩层loading效果

简介: 教你用vue自定义指令做一个组件的遮罩层loading效果

使用Vue自定义指令实现组件遮罩层Loading效果

Web开发中,加载状态(loading状态)是常见的用户交互反馈之一。当页面或组件的数据正在加载时,显示一个遮罩层可以阻止用户与当前界面进行交互,并提示用户数据正在加载中。Vue.js作为一种高效、灵活的JavaScript框架,提供了一种机制让我们能够轻松地实现这种效果,那就是自定义指令。

在本文中,我们将通过创建一个Vue自定义指令来实现组件级别的遮罩层loading效果。我们将分步骤介绍如何实现这个功能,并解释其中的关键概念。

步骤1:创建遮罩层组件

首先,我们需要创建一个遮罩层组件(MaskLayer.vue),该组件将用于在加载时覆盖在目标组件之上。

<!-- MaskLayer.vue -->
<template>
  <div v-show="visible" class="mask-layer">
    <div class="loading-spinner">
      <!-- 这里可以放置任何你想要的加载动画 -->
      <span class="spinner"></span>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    visible: {
      type: Boolean,
      default: false
    }
  }
};
</script>

<style scoped>
.mask-layer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(255, 255, 255, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999;
}

.loading-spinner {
  /* 自定义加载动画样式 */
}
</style>

步骤2:注册自定义指令

接下来,我们需要在Vue实例中注册一个自定义指令,用于控制遮罩层的显示和隐藏。

// main.js 或其他Vue实例初始化文件
import Vue from 'vue';
import MaskLayer from './components/MaskLayer.vue';

Vue.directive('loading', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el, binding) {
    // 创建遮罩层组件实例并挂载到DOM
    const maskLayer = new Vue({
      render: (h) => h(MaskLayer, {
        props: {
          visible: false // 初始状态设置为隐藏
        }
      })
    }).$mount();

    // 将遮罩层添加到目标元素的父级,并设置样式以确保它覆盖目标元素
    el.parentNode.insertBefore(maskLayer.$el, el);

    // 保存遮罩层实例以便后续操作
    el._maskLayer = maskLayer;

    // 根据binding.value的变化控制遮罩层的显示与隐藏
    el._maskLayerVisible = binding.value;

    // 监听binding的变化
    el._maskLayerBinding = binding;
    binding.instance.$watch(binding.expression, function(newVal, oldVal) {
      if (newVal !== oldVal) {
        el._maskLayer.visible = newVal;
      }
    });
  },
  // 当指令从元素上解绑时
  unbind: function (el) {
    // 移除遮罩层
    if (el._maskLayer && el._maskLayer.$el && el._maskLayer.$el.parentNode) {
      el._maskLayer.$el.parentNode.removeChild(el._maskLayer.$el);
    }
  }
});

在上面的代码中,我们注册了一个名为loading的自定义指令,该指令在插入元素到DOM时创建并插入遮罩层组件,并根据指令绑定的值控制遮罩层的显示与隐藏。

然而,上述实现存在一个问题,即inserted钩子只会在元素初次插入DOM时被调用一次,而我们希望在绑定的值变化时也能更新遮罩层的显示状态。因此,我们需要稍微修改一下实现方式。

步骤3:改进自定义指令

为了响应绑定的值的变化,我们应该使用componentUpdated钩子而不是inserted钩子,并且我们需要处理遮罩层的显示逻辑,确保它正确地跟随绑定的值变化。

我们将采用动态组件和v-if来控制遮罩层的显示和隐藏,而不是直接操作DOM。

首先,修改遮罩层组件,使其能够接受动态属性来控制显示状态:

<!-- MaskLayer.vue -->
<template>
  <div v-if="show" class="mask-layer">
    <!-- 加载动画 -->
  </div>
</template>

<script>
export default {
  props: {
    show: {
      type: Boolean,
      default: false
    }
  }
};
</script>

<!-- 样式代码略 -->

然后,修改自定义指令的实现:

// main.js 或其他Vue实例初始化文件
import Vue from 'vue';
import MaskLayer from './components/MaskLayer.vue';

Vue.directive('loading', {
  // 当绑定元素插入到 DOM 中。
  inserted: function (el, binding) {
    // 插入遮罩层组件
    const maskLayerComponent = new Vue({
      render(h) {
        return h(MaskLayer, {
          props: {
            show: false
          }
        });
      }
    }).$mount();

    // 插入遮罩层DOM元素
    el.parentNode.insertBefore(maskLayerComponent.$el, el);

    // 在el上设置一个属性来存储遮罩层实例
    el._maskLayer = maskLayerComponent;

    // 初始设置遮罩层显示状态
    update(el, binding);
  },
  // 组件更新
  componentUpdated: function (el, binding) {
    // 更新遮罩层显示状态
    update(el, binding);
  },
  // 当指令从元素上解绑时
  unbind: function (el) {
    // 移除遮罩层
    if (el._maskLayer && el._maskLayer.$el && el._maskLayer.$el.parentNode) {
      el._maskLayer.$el.parentNode.removeChild(el._maskLayer.$el);
    }
  }
});

// 更新遮罩层显示状态的函数
function update(el, binding) {
  if (binding.value) {
    el._maskLayer.show = true;
  } else {
    el._maskLayer.show = false;
  }
}

现在,我们的自定义指令v-loading已经能够在绑定的值变化时动态地显示和隐藏遮罩层了。

步骤4:在组件中使用自定义指令

最后,我们可以在任何组件或元素上使用v-loading指令,并通过绑定一个布尔值来控制遮罩层的显示和隐藏。

<template>
  <div>
    <!-- 其他内容 -->

    <!-- 调用自定义指令 -->
    <button @click="toggleLoading">加载数据</button>
    <div v-loading="isLoading">
      <!-- 这里是需要被遮罩层覆盖的内容 -->
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isLoading: false
    };
  },
  methods: {
    toggleLoading() {
      this.isLoading = !this.isLoading;
      // 模拟异步加载
      setTimeout(() => {
        this.isLoading = false;
      }, 2000);
    }
  }
};
</script>

在上面的示例中,当用户点击按钮时,isLoading状态会在truefalse之间切换,从而控制遮罩层的显示和隐藏。

这样,我们就成功使用Vue自定义指令实现了一个组件级别的遮罩层loading效果。这种方法的好处是它是可复用的,并且可以与任何组件或元素无缝集成,提供了一种灵活且可维护的方式来增强用户体验。

简单案例

时间有限,不上图了。

<div class="w-box-connent" v-whr-loading="isloading"></div>

大概意思就是,有一个div,当isloading==true时候我要给这个div上面蒙上一层遮罩层。

Vue.directive('whr-loading', {
        bind(el, bindingvnode, vnode) {
            let divzhezhao = document.createElement('div')
            divzhezhao.setAttribute('class', 'zhezhao')
            let divbaifenbai = document.createElement('div')
            divbaifenbai.setAttribute('class', 'baifenbai')
            let i = document.createElement('i')
            i.setAttribute('class', 'el-icon-loading')
            divzhezhao.append(divbaifenbai, i)
            el.insertBefore(divzhezhao, el.children[0])
            divzhezhao.setAttribute('style', 'position: absolute;\n' +
                '      width: 100%;\n' +
                '      height: 100%;\n' +
                '      background-color: #0f7188b8;\n' +
                '      text-align: center;\n' +
                '      color:#ffffff96;\n' +
                '      font-size: 12px;')
            divbaifenbai.setAttribute('style','height: 100%;\n' +
                '       width: 0px;\n' +
                '       display: inline-block;\n' +
                '       vertical-align: middle;')
            if (bindingvnode.value == false){
                divzhezhao.style.display = 'none'
            }else {
                divzhezhao.style.display = 'inline'
            }
        },
        update(el, binding, vnode){
            if (binding.value == false){
                el.children[0].style.display = 'none'

            }else {
                el.children[0].style.display = 'inline'
            }
        }
    })

这里的el-icon-loading就是一个小圈在转那个样式。

快去试试吧!

注意这是2.0写法,3.0的同学改一下函数名称就好了。

5. 优化与扩展

5.1 全局遮罩层与局部遮罩层

上面的实现中,遮罩层是相对于被绑定元素局部显示的。如果需要全局遮罩层,则可以将遮罩层直接添加到document.body上,而不是el.parentNode

5.2 自定义样式和类名

可以通过指令的binding.argbinding.modifiers来传递额外的参数,如自定义样式或类名。

5.3 指令的双向绑定

虽然Vue指令不支持双向绑定,但你可以通过自定义事件和v-model来模拟这一行为,从而更灵活地控制遮罩层。

5.4 遮罩层下的滚动行为

当遮罩层显示时,可能需要禁止页面滚动。这可以通过在遮罩层显示时给body添加overflow: hidden样式,并在遮罩层隐藏时移除该样式来实现。

5.5 动画和过渡效果

可以通过Vue的<transition>组件来增强遮罩层的显示和隐藏动画。

5.6 指令的兼容性

确保你的自定义指令在所有目标浏览器上都能正常工作。特别是使用了较新的JavaScript特性时,需要考虑转译或提供polyfill。

相关文章
|
7天前
|
移动开发 JavaScript API
Vue Router 核心原理
Vue Router 是 Vue.js 的官方路由管理器,用于实现单页面应用(SPA)的路由功能。其核心原理包括路由配置、监听浏览器事件和组件渲染等。通过定义路径与组件的映射关系,Vue Router 将用户访问的路径与对应的组件关联,支持哈希和历史模式监听 URL 变化,确保页面导航时正确渲染组件。
|
10天前
|
监控 JavaScript 前端开发
ry-vue-flowable-xg:震撼来袭!这款基于 Vue 和 Flowable 的企业级工程项目管理项目,你绝不能错过
基于 Vue 和 Flowable 的企业级工程项目管理平台,免费开源且高度定制化。它覆盖投标管理、进度控制、财务核算等全流程需求,提供流程设计、部署、监控和任务管理等功能,适用于企业办公、生产制造、金融服务等多个场景,助力企业提升效率与竞争力。
62 12
|
7天前
|
JavaScript 前端开发 开发者
Vue中的class和style绑定
在 Vue 中,class 和 style 绑定是基于数据驱动视图的强大功能。通过 class 绑定,可以动态更新元素的 class 属性,支持对象和数组语法,适用于普通元素和组件。style 绑定则允许以对象或数组形式动态设置内联样式,Vue 会根据数据变化自动更新 DOM。
|
7天前
|
JavaScript 前端开发 数据安全/隐私保护
Vue Router 简介
Vue Router 是 Vue.js 官方的路由管理库,用于构建单页面应用(SPA)。它将不同页面映射到对应组件,支持嵌套路由、路由参数和导航守卫等功能,简化复杂前端应用的开发。主要特性包括路由映射、嵌套路由、路由参数、导航守卫和路由懒加载,提升性能和开发效率。安装命令:`npm install vue-router`。
|
JavaScript
Vue的非父子组件之间传值
全局事件总线 一种组件间通信的方式,适用于任意组件间通信
|
缓存 JavaScript 前端开发
Vue Props、Slot、v-once、非父子组件间的传值....
Vue Props、Slot、v-once、非父子组件间的传值....
102 0
|
JavaScript
Vue中父子组件传值
先在⽗组件中给⼦组件的⾃定义属性绑定⼀个⽗组件的变量
|
JavaScript
vue 组件传值
vue 组件传值
97 0
|
JavaScript
vue父子组件传值
vue父子组件传值
|
JavaScript
vue兄弟组件传值 方便快捷
vue兄弟组件传值 方便快捷

热门文章

最新文章