vue 自定义指令

246 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第12天,点击查看活动详情

1.vue 自定义指令属性信息

指令定义函数提供了几个钩子函数 (可选)

  • bind : 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。
  • inserted : 被绑定元素插入父节点时调用 (父节点存在即可调用,不必存在于 document 中)。
  • update : 所在组件的 VNode 更新时调用,但是可能发生在其孩子的 VNode 更新之前。指令的值可能发生了改变也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
  • componentUpdated : 所在组件的 VNode 及其孩子的 VNode 全部更新时调用。
  • unbind : 只调用一次,指令与元素解绑时调用。

钩子函数被赋予了以下参数

  • el : 指令所绑定的元素,可以用来直接操作 DOM 。
  • binding : 一个对象,包含以下属性:
  • name : 指令名,不包括v-前缀。
  • value : 指令的绑定值,例如:v-my-directive=“1 + 1”, value 的值是2。
  • oldValue : 指令绑定的前一个值,仅在update和componentUpdated钩子中可用。无论值是否改变都可用。
  • expression : 绑定值的字符串形式。例如v-my-directive=“1 + 1”,expression 的值是"1 + 1"。
  • arg : 传给指令的参数。例如v-my-directive:foo,arg 的值是"foo"。
  • modifiers : 一个包含修饰符的对象。例如:v-my-directive.foo.bar, 修饰符对象 modifiers 的值是{ foo: true, bar: true }。
  • vnode : Vue 编译生成的虚拟节点,查阅 VNode API 了解更多详情。
  • oldVnode : 上一个虚拟节点,仅在update和componentUpdated钩子中可用。
2.vue 全局自定义指令
1). 在main.js内部写入
// 在main.js内部写入
Vue.directive('inputFn',{
  // 钩子函数,被绑定元素插入父节点时调用 (父节点存在即可调用,不必存在于 document 中)。
  inserted(el){
    // 聚焦元素
    el.focus()
    console.log( 'inserted' );
  },
  // 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。
  bind(){
    console.log( 'bind' );
  },
  // 所在组件的 VNode 更新时调用,但是可能发生在其孩子的 VNode 更新之前。
  // 指令的值可能发生了改变也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 
  update(){
    console.log( 'update' );
  },
  // 所在组件的 VNode 及其孩子的 VNode 全部更新时调用。
  componentUpdated(){
    console.log( 'componentUpdated' );
  },
  // 只调用一次,指令与元素解绑时调用。
  unbind(){
    console.log( 'unbind' );
  }
})

2). 需要在main.js文件中引入

输入框只允许输入数字

// checkDigit.js
const checkDigit = {}
checkDigit.install = Vue => {
  Vue.directive('checkDigit', {
    inserted(el, binding) {
      // 节点信息
      const inputValue = el.getElementsByTagName('input')[0]
      el.onkeyup = (event) => {
        // 把不是数字的替换掉
        inputValue.value = inputValue.value.replace(/[^0-9]/g, '')
      }
    }
  })
}
export default checkDigit

// main.js 内部 sunNumber引进来
import checkDigit from './checkDigit'
Vue.use(checkDigit)
3). 批量自定义指令引入
  1. copy 复制目标值
  2. debounce 复制目标值
// directive.js
const copy = {
  bind(el, { value }) {
    el.$value = value
    el.hendler = () => {
      if (!el.$value) {
        // 值为空跳出去不执行
        console.log('空值啥也没有')
        return;
      }
      // 动态创建textarea 标签
      const textarea = document.createElement('textarea')
      // 将 textarea 设为 readonly 防止 ios 下自动唤起键盘,同时将 textarea 移除可视区域
      textarea.readOnly = 'readonly'
      textarea.style.position = 'absolute'
      textarea.style.left = '-9999999999px'
      textarea.value = el.$value
      // 将 textarea 插入到 body 中
      document.body.appendChild(textarea)
      // 选中值并复制
      textarea.select()
      const result = document.execCommand('Copy')
      if (result) {
        console.log('复制成功')
      }
      document.body.removeChild(textarea)
    }
    // 绑定点击事件,就是一键复制
    el.addEventListener('click', el.hendler)
  },
  // 当传经来的值更新的时候触发
  componentUpdated(el, { value }) {
    el.$value = value
  },
  // 指令与元素解绑的时候,移除事件绑定
  unbind(el) {
    el.removeEventListener('click', el.hendler)
  }
}
const debounce = {
  inserted: function (el, binding) {
    let timer
    // 防止多次触发当前元素的点击事件
    el.addEventListener('click', () => {
      if (timer) {
        clearTimeout(timer)
      }
      timer = setTimeout(() => {
        binding.value()
      }, 1000)
    })
  }
}
const directiveData = {
  copy,
  debounce
}
export default {
  install(Vue) {
    Object.keys(directiveData).forEach(item => {
      Vue.directive(item, directiveData[item])
    })
  }
}
// main.js
import directive from './directive'
Vue.use(directive)
3.vue 局部自定义指令
  1. 在自己需要的.vue文件中内部
<template>
  <div>
    <!-- 页面打开的时候就获得焦点 -->
    <input type="text" v-inputFocus />
  </div>
</template>
<script>
export default {
  directives:{
    // 自定义指令的名字
    inputFocus:{
      // 钩子函数,被绑定元素插入父节点时调用 (父节点存在即可调用,不必存在于 document 中)。
      inserted(el){
        el.focus() // 元素获取焦点时所触发的事件
        console.log( 'inserted' );
      },
      // 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。
      bind(){
        console.log( 'bind' );
      },
      // 所在组件的 VNode 更新时调用,但是可能发生在其孩子的 VNode 更新之前。
      // 指令的值可能发生了改变也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 
      update(){
        console.log( 'update' );
      },
      // 所在组件的 VNode 及其孩子的 VNode 全部更新时调用。
      componentUpdated(){
        console.log( 'componentUpdated' );
      },
      // 只调用一次,指令与元素解绑时调用。
      unbind(){
        console.log( 'unbind' );
      }
      
    },
    "scroll-select": {
      // 指令的名称
      bind(el, binding) {
        const SELECTWRAP = el.querySelector(".el-select-dropdown .el-select-dropdown__wrap");
        SELECTWRAP.addEventListener("scroll", function () {
          // scrollTop 这里可能因为浏览器缩放存在小数点的情况,导致了滚动到底部时
          // scrollHeight 减去滚动到底部时的scrollTop ,依然大于clientHeight 导致无法请求更多数据
          // 这里将scrollTop向上取整 保证滚到底部时,触发调用
          const CONDITION = this.scrollHeight - Math.ceil(this.scrollTop) <= this.clientHeight;
          // el.scrollTop !== 0 当输入时,如果搜索结果很少,以至于没看到滚动条,那么此时的CONDITION计算结果是true,会执行bind.value(),此时不应该执行,否则搜索结果不匹配
          if (CONDITION && this.scrollTop !== 0) {
            const Elvalue = el.querySelector(".el-input__inner") || el.querySelector(".el-select__input");
            binding.value(Elvalue.value);
          }
        });
      },
    },
  },
  data () {
    return {}  
  }
  mounted(){},
}
</script>