Skip to content

自定义指令

关于Vue中指令的详细介绍可以查看Vue文档,这里主要记录如何实现一个符合需求的自定义指令。

tips: 自定义指令主要用于对DOM元素进行操作。

需求: 实现一个完全覆盖页面上指定区域的遮罩,遮罩不能影响除指定区域外的其他元素,且可以动态切换此遮罩的开启、关闭。

思考: 利用自定义指令实现,获取指令绑定元素的宽高,若该元素有子元素,需要包含其所有子元素;根据指令绑定值,动态创建/销毁遮罩元素。

钩子函数

一个指令定义对象可以提供如下几个可选的钩子函数

  • bind: 只调用一次,指令第一次绑定到元素时调用。(可以做一些一次性的初始化设置
  • inserted: 被绑定元素插入父节点时调用 ,仅保证父节点存在,但不一定已被插入文档中(在此创建并插入DOM元素)。
  • update: 所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。(可以通过比较更新前后的值来做一些操作
  • componentUpdated: 指令所在组件的 VNode 及其子 VNode 全部更新后调用 (组件内所有DOM元素都已更新,可以获取指令所在元素包含子元素在内的全部高度)
  • unbind: 只调用一次,指令与元素解绑时调用(可以在此移除/销毁不再需要的DOM元素)。

钩子函数参数

全部参数信息可见文档

实现

可以在项目目录src/directives下新建mask目录,文件结构如下

├─src
   ├─directives     
   │  ├─mask
   │  │      index.js
   │  │      mask.js
  • mask.js中导出包含钩子函数的对象
查看代码
js
// 创建并插入遮罩
function insertMaskEl(el) {
  const parentNode = el.parentNode
  const fragment = document.createDocumentFragment()
  el.mask = document.createElement(el.tagName)
  parentNode.style.position = 'relative'
  // 样式可以通过添加类名统一添加,也可以直接改变元素style属性
  el.mask.classList.add('mask-wrapper')
  fragment.appendChild(el.mask)
  parentNode.appendChild(fragment)
}
// 移除并销毁遮罩
function removeMaskEl(el) {
  el.mask && el.parentNode.removeChild(el.mask)
}

export default {
  inserted(el, binding, vnode) {
    // 根据指令的绑定值切换遮罩的状态
    if (binding.value) {
      insertMaskEl(el)
    } else {
      removeMaskEl(el)
    }
  },

  // 指令值变更时,更新遮罩
  update(el, binding) {
    if (binding.value !== binding.oldValue) {
      insertMaskEl(el)
    }
  },

  // DOM元素完全更新,此处可获取到指令所在组件完整的高度
  componentUpdated(el, binding) {
    if (el.mask) {
      el.mask.style.height = el.getBoundingClientRect().height.toFixed(1) + 'px'
    }
  },

  unbind(el) {
    removeMaskEl(el)
  }
}
css
/* 遮罩样式,全局指令需要将样式写在公共的样式文件中 */
.mask-wrapper {
  width: 100%;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 99;
  opacity: 0;
}
  • index.js中注册指令
js
import Vue from 'vue'

// 全局注册
Vue.directive('mask', mask)
  • src/main.js中导入文件:可以单独导入@/src/directives/mask,也可以和其他自定义指令一起导入。

  • 使用

js
<template>
  <div v-mask="showMask">
    <div></div>
    <p></p>
  </div>
</template>

export default {
  data() {
    return {
      showMask: false // 为true时显示遮罩
    }
  }
}

Updated at: