Vue自定义指令及input框的防抖和节流

504 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第25天,点击查看活动详情

什么是自定义指令?

常用的一些内置指令有 v-modelv-showv-if等,除了这些内置指令,Vue也允许注册自定义指令。

举个栗子,当页面在加载出来时,就让搜索框获得焦点,用自定义指令来实现这个功能:

// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})

如果想注册局部指令,在实例中写入 directives选项:

new Vue({
    el:"#app",
    directives:{
        focus:{
            // 当被绑定的元素插入到 DOM 中时
            inserted: function (el) {
                // 聚焦元素
                el.focus()
            }
        }
    }
})

然后就可以在页面元素中使用自定义的 v-focus 指令了:

<input v-focus>

钩子函数

一个指令定义对象可以提供如下几个钩子函数 (均为可选):

  • bind: 只调用一次,指令第一次绑定到元素时调用,可以定义一个在绑定时执行一次的初始化动作。
  • inserted: 被绑定元素插入父节点时调用。
  • update: 所在组件的任意VNode更新就会被调用,不论绑定值是否发生变化,可以通过对比更新前后的值来忽略不必要的更新。
  • componentUpdated: 指令所在组件的 VNode 及其子VNode全部更新后调用。
  • unbind: 只调用一次, 指令与元素解绑时调用。

钩子函数参数

指令钩子函数会被传入以下参数:

  • el:指令所绑定的元素,可以用来直接操作 DOM。

  • binding

    :一个对象,包含以下 property:

    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值,例如:v-button="submit" 中,绑定值为 submit
    • oldValue:指令绑定的前一个值,仅在 updatecomponentUpdated 钩子中可用。无论值是否改变都可用。
    • expression:字符串形式的指令表达式。例如 v-button="‘submit’,2" 中,表达式为 "‘submit’,2"
    • arg:传给指令的参数,可选。例如 v-button.debounce:foo 中,参数为 "foo"
    • modifiers:一个包含修饰符的对象。例如:v-button.debounce:foo 中,修饰符对象为 { debounce: true }
  • vnode:Vue 编译生成的虚拟节点。

  • oldVnode:上一个虚拟节点,仅在 updatecomponentUpdated 钩子中可用。

input框的防抖和节流

<div id="app">
    <input type="text" v-model="debounce" v-debounce="{event:'input',fn:fn,delay:1000}">
    <input type="text" v-model="throttle" v-throttle="{event:'input',fn:fn,delay:1000}">
</div>
//防抖
Vue.directive("debounce", {
    inserted: function (el, binding) {
        //事件名、执行的函数、延迟时间
        let { event, fn, delay } = binding.value;
        let timer
        let flag = true;
        //中文输入法下不触发input事件
        if (event === "input") {
            el.addEventListener("compositionstart", () => {
                flag = false;
            })
            el.addEventListener("compositionend", () => {
                flag = true;
            })
        }
        el.addEventListener(event, () => {
            timer && clearTimeout(timer)
            timer = setTimeout(() => {
                if (flag) fn("debounce");
            }, delay)
        })
    }
});
//节流
let last;
Vue.directive("throttle", {
    bind(el, binding, vnode) {
        last = Date.now();
    },
    inserted: function (el, binding) {
        let { event, fn, delay } = binding.value;
        let flag = true;
        if (event === "input") {
            el.addEventListener("compositionstart", () => {
                flag = false;
            })
            el.addEventListener("compositionend", () => {
                flag = true;
            })
        }
        el.addEventListener(event, () => {
            let now = Date.now();
            let { event, fn, delay } = binding.value
            if (now - last >= delay) {
                last = now;
                if (flag) fn("throttle");
            }
        })
    },
});
new Vue({
    el: '#app',
    data: {
        debounce: 'debounce!',
        throttle: 'throttle!',
    },
    methods: {
        fn: function (val) {
            console.log(val + "执行了");
        }
    },
})