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

相关文章
|
1天前
|
JavaScript
如何在 Vue 项目中选择合适的模块格式
【10月更文挑战第20天】选择合适的模块格式需要综合考虑多个因素,没有一种绝对正确的选择。需要根据项目的具体情况进行权衡和分析。在实际选择过程中,要保持灵活性,根据项目的发展和变化适时调整模块格式。
14 7
|
1天前
|
JavaScript 前端开发 编译器
在 Vue 项目中使用 ES 模块格式的优点
【10月更文挑战第20天】在 Vue 项目中使用 ES 模块格式具有众多优点,这些优点共同作用,使得项目能够更高效、更可靠地开发和运行。当然,在实际应用中,还需要根据项目的具体情况和需求进行合理的选择和配置。
14 6
|
1天前
|
JavaScript 前端开发 开发者
Vue 的优缺点
【10月更文挑战第16天】Vue 具有众多优点,使其成为前端开发中备受青睐的框架之一。尽管它也存在一些局限性,但通过合理的应用和技术选型,这些问题可以得到一定程度的解决。在实际项目中,开发者可以根据项目的需求和特点,权衡 Vue 的优缺点,选择最适合的技术方案。同时,随着 Vue 不断的发展和完善,相信它将在前端开发领域继续发挥重要作用。
11 6
|
1天前
|
JavaScript 前端开发 Java
《vue3第五章》新的组件,包含:Fragment、Teleport、Suspense
《vue3第五章》新的组件,包含:Fragment、Teleport、Suspense
10 2
|
1天前
|
Java
vue3知识点:Teleport组件
vue3知识点:Teleport组件
10 1
|
JavaScript 数据安全/隐私保护
Vue:几个实用的 Vue 自定义指令
Vue:几个实用的 Vue 自定义指令
110 0
Vue:几个实用的 Vue 自定义指令
|
3天前
|
JavaScript
在 Vue 中处理组件选项与 Mixin 选项冲突的详细解决方案
【10月更文挑战第18天】通过以上的分析和探讨,相信你对在 Vue 中使用 Mixin 时遇到组件选项与 Mixin 选项冲突的解决方法有了更深入的理解。在实际开发中,要根据具体情况灵活选择合适的解决方案,以确保代码的质量和可维护性。
27 7
|
3天前
|
存储 JavaScript
vue——store全局存储
【10月更文挑战第18天】Vuex 是 Vue.js 应用中非常重要的一个工具,它为我们提供了一种有效的状态管理方式。通过合理地使用 Vuex,我们可以更好地组织和管理应用的状态,提高应用的开发效率和质量。
15 1
|
2天前
|
缓存 JavaScript UED
Vue 的动态组件与 keep-alive
【10月更文挑战第19天】总的来说,动态组件和 `keep-alive` 是 Vue.js 中非常实用的特性,它们为我们提供了更灵活和高效的组件管理方式,使我们能够更好地构建复杂的应用界面。深入理解和掌握它们,以便在实际开发中能够充分发挥它们的优势,提升我们的开发效率和应用性能。
29 18
|
1天前
|
JavaScript 索引
vue 在 v-for 时给每项元素绑定事件
在 Vue 中使用 v-for 渲染列表时,可以通过给每项元素绑定事件来实现交互功能。通常使用 `@click` 等事件修饰符,结合方法或内联表达式来处理事件。例如:`&lt;li v-for=&quot;item in items&quot; @click=&quot;handleClick(item)&quot;&gt;{{ item }}&lt;/li&gt;`。