教你用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。

相关文章
|
5月前
|
JavaScript
Vue中如何实现兄弟组件之间的通信
在Vue中,兄弟组件可通过父组件中转、事件总线、Vuex/Pinia或provide/inject实现通信。小型项目推荐父组件中转或事件总线,大型项目建议使用Pinia等状态管理工具,确保数据流清晰可控,避免内存泄漏。
481 2
|
4月前
|
缓存 JavaScript
vue中的keep-alive问题(2)
vue中的keep-alive问题(2)
406 137
|
7月前
|
人工智能 JSON JavaScript
VTJ.PRO 首发 MasterGo 设计智能识别引擎,秒级生成 Vue 代码
VTJ.PRO发布「AI MasterGo设计稿识别引擎」,成为全球首个支持解析MasterGo原生JSON文件并自动生成Vue组件的AI工具。通过双引擎架构,实现设计到代码全流程自动化,效率提升300%,助力企业降本增效,引领“设计即生产”新时代。
556 1
|
7月前
|
JavaScript 安全
在 Vue 中,如何在回调函数中正确使用 this?
在 Vue 中,如何在回调函数中正确使用 this?
369 0
|
JavaScript
Vue的非父子组件之间传值
全局事件总线 一种组件间通信的方式,适用于任意组件间通信
233 0
|
缓存 JavaScript 前端开发
Vue Props、Slot、v-once、非父子组件间的传值....
Vue Props、Slot、v-once、非父子组件间的传值....
211 0
|
JavaScript
Vue中父子组件传值
先在⽗组件中给⼦组件的⾃定义属性绑定⼀个⽗组件的变量
214 0
|
JavaScript
vue 组件传值
vue 组件传值
182 0
|
JavaScript
vue父子组件传值
vue父子组件传值
|
JavaScript
vue兄弟组件传值 方便快捷
vue兄弟组件传值 方便快捷

热门文章

最新文章