Vue自定义指令怎么用,怎么写

765 阅读3分钟

Vue自定义指令使用记录

最近在研究自定义指令,开始使用的话遇到点小问题。慢慢看大佬的文章也慢慢的理解了。 首先就得掏出大佬封装的方法。后续的话自己写一点也不是问题。首先介绍的是全局指令,局部指令用的地方比较少。

自定义指令的钩子函数(生命周期)

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  • inserted:被绑定元素插入父节点时调用(仅保证父节点存在,但不一定被插入文档中)
  • update:所在组件的vnode更新时调用,但是可能发生在子节点更新前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前 后的值来判断你接下来要做的工作
  • componentUpdated:指令所在组件的vnode及其子vnode全部更新后调用
  • unbind:只调用一次,指令与元素解绑时调用。

指令钩子包含以下参数

  • el:指令所绑定的元素,可以用来直接操作 DOM。
  • binding:一个对象,包含以下 property:
  1. name:指令名,不包括 v- 前缀。
  2. value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
  3. oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
  4. expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
  5. arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
  6. modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
  • vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

生命周期应该类似(具体我也不太清楚,欢迎指正)

组件生命自定义指令生命
createdbind
mountedinserted
BeforUpdateupdate
UpdatecomponentUpdated
destroyunbind

1.首先在src下建立directives/index.js

import Vue from "vue"
// 防抖钩子
Vue.directive('debounce', {
        inserted: function(el, binding) {
            let timer
            el.addEventListener('click', () => {
                if (timer) {
                    clearTimeout(timer)
                }
                timer = setTimeout(() => {
                    binding.value()
                }, 1000)
            })
        },
    })
    
// 验证input输入
let findEle = (parent, type) => {
    return parent.tagName.toLowerCase() === type ? parent : parent.querySelector(type)
}

const trigger = (el, type) => {
    const e = document.createEvent('HTMLEvents')
    e.initEvent(type, true, true)
    el.dispatchEvent(e)
}

Vue.directive('emoji', {
    bind: function(el) {
        // 正则规则可根据需求自定义
        var regRule = /[^\u4E00-\u9FA5|\d|\r\n\s,.?!,。?!…—&$=()-+/*{}[\]]|\s/g;
        let $inp = findEle(el, 'input');
        el.$inp = $inp;
        $inp.handle = function() {
            let val = $inp.value;
            $inp.value = val.replace(regRule, '');
            trigger($inp, 'input');
        }
        $inp.addEventListener('keyup', $inp.handle);
    },
    unbind: function(el) {
        el.$inp.removeEventListener('keyup', el.$inp.handle);
    },
})

// 添加水印
function addWaterMarker(str, parentNode, font, textColor) {
    // 水印文字,父元素,字体,文字颜色
    var can = document.createElement('canvas')
    parentNode.appendChild(can);
    can.width = 200;
    can.height = 150;
    can.style.display = 'none';
    var cans = can.getContext('2d');
    cans.rotate((-20 * Math.PI) / 180);
    cans.font = font || '16px Microsoft JhengHei';
    cans.fillStyle = textColor || 'rgba(180, 180, 180, 0.3)';
    cans.textAlign = 'left';
    cans.textBaseline = 'Middle';
    cans.fillText(str, can.width / 10, can.height / 2);
    parentNode.style.backgroundImage = 'url(' + can.toDataURL('image/png') + ')';
}

Vue.directive('waterMarker', {
    bind: function(el, binding) {
        addWaterMarker(binding.value.text, el, binding.value.font, binding.value.textColor);
    }
})

// 长按事件
Vue.directive('longpress', {
        bind: function(el, binding) {
            if (typeof binding.value !== 'function') {
                throw 'callback must be a function'
            }
            // 定义变量
            let pressTimer = null
                // 创建计时器( 2秒后执行函数 )
            let start = (e) => {
                    if (e.type === 'click' && e.button !== 0) {
                        return
                    }
                    if (pressTimer === null) {
                        pressTimer = setTimeout(() => {
                            handler()
                        }, 2000)
                    }
                }
                // 取消计时器
            let cancel = () => {
                    if (pressTimer !== null) {
                        clearTimeout(pressTimer)
                        pressTimer = null
                    }
                }
                // 运行函数
            const handler = (e) => {
                    binding.value(e)
                }
                // 添加事件监听器
            el.addEventListener('mousedown', start)
            el.addEventListener('touchstart', start)
                // 取消计时器
            el.addEventListener('click', cancel)
            el.addEventListener('mouseout', cancel)
            el.addEventListener('touchend', cancel)
            el.addEventListener('touchcancel', cancel)
        },
        // 当传进来的值更新的时候触发
        componentUpdated(el, {
            value
        }) {
            el.$value = value
        },
        // 指令与元素解绑的时候,移除事件绑定
        unbind(el) {
            el.removeEventListener('click', el.handler)
        },
    })
    
// 权限校验判断是否展示
function checkArray(key) {
    let arr = ['1', '2', '3', '4']
    let index = arr.indexOf(key)
    if (index > -1) {
        return true // 有权限
    } else {
        return false // 无权限
    }
}
// 权限校验
Vue.directive('permission', {
    inserted: function(el, binding) {
        let permission = binding.value // 获取到 v-permission的值
        if (permission) {
            let hasPermission = checkArray(permission)
            if (!hasPermission) {
                // 没有权限 移除Dom元素
                el.parentNode && el.parentNode.removeChild(el)
            }
        }
    },
})

2.使用方法(我是全局的自定义指令)

在main.js里面(注意要在new Vue之前

image.png

使用的话直接 v-指令名称就行了

  1. 有参数 v-指令名="123" 如(v-html="xxx")
  2. 没参数 v-指令名 如(v-focus)