Vue 3 自定义指令实现

31 阅读21分钟

首先,我们得回忆一下Vue 3的自定义指令的写法。Vue 3中,自定义指令的钩子函数和Vue 2有所不同,

vue2自定义指令的钩子函数

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。

  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。

  • unbind:只调用一次,指令与元素解绑时调用。

vue3自定义指令的钩子函数

  • created(el, binding, vnode) // 在元素被插入到 DOM 前调用
  • beforeMount(el, binding, vnode) // 在绑定元素的父组件
  • mounted(el, binding, vnode) // 及他自己的所有子节点都挂载完成后调用
  • beforeUpdate(el, binding, vnode, prevVnode) // 绑定元素的父组件更新前调用
  • updated(el, binding, vnode, prevVnode) // 及他自己的所有子节点都更新后调用 - beforeUnmount(el, binding, vnode)// 绑定元素的父组件卸载前调用 -unmounted(el, binding, vnode)// 绑定元素的父组件卸载后调用

现在有beforeMount、mounted、beforeUpdate、updated等,不过常用的可能是mounted和updated。

防抖的目的是让某个函数在连续触发时,只在最后一次触发后的一段时间后执行一次。常见的应用场景比如输入框输入时搜索建议,防止频繁请求接口。

那用户的需求应该是指令可以应用在输入框或者按钮上,当用户频繁操作时,比如输入或者点击,防抖指令能确保处理函数不会过于频繁执行。

首先,我需要确定指令的用法,比如v-debounce,可能需要绑定事件类型和回调函数,或者时间间隔。比如:<input v-debounce:input="onInput" />,或者更灵活一点,允许指定时间和事件类型,比如v-debounce:300ms="handler"。

然后,自定义指令的结构。在Vue 3中,自定义指令通过directives选项注册,或者全局注册。指令的定义对象包含几个钩子函数,比如mounted和beforeUnmount(可能需要清理定时器)。

在mounted钩子中,需要给元素添加事件监听器,并且在事件触发时执行防抖处理。这里需要创建一个防抖函数,每次事件触发时,清除之前的定时器,重新设置一个新的。

比如,当元素挂载时,绑定input事件,事件处理函数用防抖包装用户传入的方法。同时,在组件卸载前,可能需要移除事件监听器,不过如果使用匿名函数的话,可能无法正确移除,所以可能需要保存防抖函数的引用。

另外,用户可能希望自定义防抖的时间,比如默认300毫秒,但可以传入参数修改。这时候可以通过指令的参数来设置时间,比如v-debounce:500,或者通过修饰符,或者通过绑定的值传递参数。

比如指令的值可以是一个对象,包含handler和time。或者更简单的方式,指令的参数是延迟时间,值是对应的处理函数。例如v-debounce:500="onInput",这样在指令中可以通过binding.arg获取到500,作为延迟时间,binding.value就是处理函数。

那具体实现的话,可能的结构是:

在mounted钩子中,获取元素,事件类型(默认可能是input事件?或者由用户指定),然后绑定事件。比如,如果指令用在按钮的点击事件,可能需要指定事件类型,所以可能需要指令的参数来指定事件类型,或者通过修饰符。

这时候可能需要更复杂的参数解析。比如v-debounce:click.500="handler",或者使用对象语法。

可能用户希望更灵活,所以需要设计指令的参数结构。例如:

v-debounce:eventname.delay="handler"

或者通过binding的arg和修饰符来获取事件和延迟时间。或者,如果用户传递的是一个对象,包含event、handler、delay等属性,会更灵活,但使用起来可能稍显麻烦。

或者,假设用户使用指令时,元素上已经绑定了某个事件,比如@input,这时候指令可能需要自动处理该事件的防抖?这可能比较复杂,因为指令和事件监听器是分开的。

可能更简单的方式是,指令本身负责绑定事件处理函数,并处理防抖。比如:

<input v-debounce:input="onInput" delay="500" />

或者,通过指令的值传递配置。例如:

const debounceDirective = {

mounted(el, binding) {

const { handler, event = 'input', delay = 300 } = binding.value;

let timeout;

el.addEventListener(event, (...args) => {

clearTimeout(timeout);

timeout = setTimeout(() => {

handler(...args);

}, delay);

});

}

}

这样用户需要传递一个对象给指令,比如v-debounce="{ handler: onInput, event: 'input', delay: 500 }",但这样可能不太方便。或者允许不同的参数传递方式。

另一种方式是,指令的参数作为事件类型,修饰符作为延迟时间,或者参数是延迟时间,事件类型通过修饰符指定。不过Vue的修饰符是布尔值,无法传值。所以可能需要参数来指定事件类型,而延迟时间通过value的对象传递。

或者,更灵活的方式是,把事件类型作为指令的参数,延迟时间作为修饰符。但修饰符只能是键,所以可能不行。

可能更合理的做法是,指令的参数是事件类型,比如v-debounce:input,而延迟时间可以通过值来传递,或者作为修饰符的一部分。例如:

<input v-debounce:input.500="onInput" />

但Vue中修饰符是作为对象的键存储在binding.modifiers中,所以这种情况下,修饰符500会被转换为{bindmodifiers: {'500': true}},但这样处理起来不太方便,因为无法直接获取数值。所以可能参数中的第一个部分作为事件类型,第二个作为延迟时间,但这可能需要解析参数,比如用split之类的。

或者,让用户通过对象传递参数:

v-debounce="{ event: 'input', delay: 500, handler: onInput }"

这样在指令内部可以从binding.value中解构出这些属性。

不过这样在模板中使用可能不够简洁,用户可能需要写较多的代码。

所以可能需要折中的方式。例如,默认的事件类型是input,默认延迟是300ms。用户可以通过指令的参数来指定事件类型,通过修饰符来指定延迟时间,但修饰符的问题是无法传值,只能是true。例如:v-debounce:click.500="handler"

但这样可能无法处理,因为修饰符会被视为字符串,而无法直接提取500作为数值。

所以另一种方式,指令的参数可以接受事件类型,而延迟时间通过value来传递,或者作为单独的选项。

可能更好的方式是将指令的参数作为事件类型,delay作为值中的一个属性,或者将值直接作为处理函数,而delay通过参数传递。例如:v-debounce:input.500="handler"

这里,参数部分可能是input,而500可能作为另一个参数,但Vue的指令参数只能是一个单一的字符串,无法通过点号分隔。所以可能需要用另一种方式解析参数,比如用下划线或者分号分隔,例如v-debounce:input_500,然后在指令中分割参数。

不过这可能不够直观,而且Vue的指令参数语法并不支持这种多参数的情况。所以可能更好的方式是在指令的值中传递一个对象,包含事件类型、延迟时间和处理函数。或者,把处理函数作为指令的值,而其他参数通过arg和修饰符传递。

或许更简单的方式是,将事件类型作为指令的参数,延迟时间作为修饰符,但这样修饰符只能作为键,例如:

v-debounce:input.500ms="handler"

但这样修饰符会是500ms: true,需要从修饰符的键中解析出数值,但这样可能比较复杂。

看来这个问题可能需要简化。假设用户只需要处理input事件,并且可以自定义延迟时间,或者允许事件类型和延迟时间都通过指令的参数传递。

或者,可以这样设计:指令的参数是事件类型,而延迟时间通过value对象传递,或者通过修饰符传值?

可能这里需要参考一些现有的实现,或者寻找一种比较简洁的方式。

比如,在Vue的自定义指令中,可以这样使用:

<input v-debounce:input="500" @input="onInput" />

但这样的话,指令可能需要读取绑定的事件监听器,并将其替换为防抖版本。这可能比较复杂,因为指令和事件监听器是分开的。

或者,另一种方式,指令的作用是包装元素的事件处理函数,并替换为防抖版本。例如:

const debounceDirective = {

mounted(el, binding) {

const event = binding.arg || 'input'; // 事件类型,默认为input

const delay = parseInt(binding.value) || 300; // 延迟时间,默认为300ms

let timeout;

const originalHandler = el._debounceOriginalHandler; // 需要获取原来的处理函数?这可能不可行

// 这里可能需要用户将处理函数作为指令的值传递

const handler = binding.value;

el.addEventListener(event, (...args) => {

clearTimeout(timeout);

timeout = setTimeout(() => {

handler(...args);

}, delay);

});

}

}

这样,用户需要这样使用指令:

<input v-debounce:input="onInput" :delay="500" />

或者,将delay作为指令的参数的一部分,但可能不太方便。或者,用户传递的值是一个函数,而delay作为修饰符或参数。

或者,指令的值可以是一个数组,包含处理函数和delay,例如v-debounce:input="[onInput, 500]",然后在指令内部解构。

不过这样可能不够优雅。或许更好的方式是允许用户通过一个对象配置,例如:

<input v-debounce="{ event: 'input', handler: onInput, delay: 500 }" />

然后在指令中解构这个对象。

但这样在模板中需要写比较多的代码,可能用户更喜欢简洁的方式。

回到最初的问题,用户可能想要一个简单的指令,比如:

这样默认处理input事件,延迟300ms。或者允许用户指定事件和延迟,例如:

点击

这时候,指令的参数是click,修饰符是500,但修饰符会被转换为对象中的键,比如修饰符对象是{500: true},这时候如何处理?或者,将参数解析为事件类型,而延迟时间通过value传递,例如:

<button v-debounce:click="500" @click="onClick">点击

这样,指令的参数是click,值是500,然后在指令中,将事件类型作为参数,延迟时间作为值,处理函数则来自于@click绑定的事件。但这里的问题是如何获取事件处理函数,因为@click是单独的事件监听器,而指令无法直接访问它。因此,这种方法可能不可行。

因此,或许正确的方式是将处理函数作为指令的值传递,并且允许用户通过参数指定事件类型,通过修饰符或值中的属性指定延迟时间。

例如:

默认延迟300ms,或者:

但此时,修饰符是500,但binding.modifiers将包含 {500: true},所以需要从修饰符中提取数值。这时候可能需要遍历修饰符的键,找到数值部分。例如:

const delay = Object.keys(binding.modifiers)[0] || 300;

但如果有多个修饰符,可能需要更复杂的处理,或者用户只能指定一个数值作为修饰符。这可能不太可靠,但可能是一种方式。

或者,将延迟时间作为指令的参数的一部分,比如:

v-debounce:input_500="onInput"

然后在指令中,将参数用下划线分割,第一部分是事件类型,第二部分是延迟时间:

const [event, delay] = binding.arg.split('_');

不过这样在模板中使用可能不够直观,但可行。

但Vue的指令参数不支持这样的解析,需要自己处理。

综合考虑,可能最简单的实现是,指令接受事件类型作为参数,延迟时间作为值,处理函数则通过绑定值传递。例如:

<input v-debounce:input="500" @input="onInput" />

但这样指令的值是延迟时间,而处理函数是@input绑定的,但指令无法直接访问事件处理函数,因为它们是不同的绑定。因此,这种方法可能不可行。

看来正确的做法是将处理函数作为指令的值传递,同时允许配置事件类型和延迟时间。例如:

Click me

然后在指令中解构binding.value:

const { event, handler, delay } = binding.value;

这样用户需要传递一个对象,其中包含事件类型、处理函数和延迟时间。但这样可能需要在模板中写较多的代码,尤其是处理函数可能需要通过一个对象传递,可能不太方便。

或者,可以将事件类型作为参数,处理函数作为值,延迟时间作为另一个参数或者通过修饰符。比如:

Click

但Vue的指令参数只能是一个,所以这种写法可能无法正确解析。比如,参数会是"click:500",需要自己分割。

或者,使用动态参数:

<button v-debounce:[event]="handler" />

但这样动态参数可能不太方便。

看来这个问题可能需要一个折中的方案。假设用户只需要处理input事件,并且可以自定义延迟时间,那么指令可以设计为:

<input v-debounce="500" @input="onInput" />

然后指令的作用是将@input的处理函数包装成防抖的。但如何访问到用户绑定的@input处理函数呢?这可能比较复杂,因为指令无法直接访问元素上的事件监听器,除非在指令中覆盖它。

另一种方法是,指令自己绑定事件处理函数,这样用户不需要再使用@input,而是通过指令的值传递处理函数和配置。例如:

然后在指令内部绑定input事件,并在触发时调用handler,这样用户不需要再写@input。

这可能更可行,因为这样指令完全控制事件的绑定和处理。

所以,最终的实现步骤可能是:

  1. 在指令的mounted钩子中,解构binding.value,获取handler、event(默认input)、delay(默认300)。

  2. 给元素添加指定的事件监听器,当事件触发时,执行防抖处理,即在delay时间后执行handler。

  3. 在元素卸载前(beforeUnmount钩子),移除事件监听器,避免内存泄漏。

同时,需要注意防抖函数的作用域,确保每次事件触发时使用的是同一个timeout变量。可以将timeout变量保存在元素对象上,或者通过闭包保存。

比如:


const debounceDirective = {

mounted(el, binding) {

const defaultEvent = 'input';

const defaultDelay = 300;

// 如果binding.value是一个函数,说明用户只传了handler

let event = defaultEvent;

let delay = defaultDelay;

let handler = binding.value;

// 如果binding.value是对象,则解构

if (typeof binding.value === 'object') {

handler = binding.value.handler;

event = binding.value.event || defaultEvent;

delay = binding.value.delay || defaultDelay;

} else if (typeof binding.value === 'function') {

handler = binding.value;

} else {

console.error('v-debounce指令需要传递一个函数或配置对象');

return;

}

// 保存防抖的timeout ID到el对象上

el._debounceTimeout = null;

el._debounceHandler = function(...args) {

clearTimeout(el._debounceTimeout);

el._debounceTimeout = setTimeout(() => {

handler(...args);

}, delay);

};

el.addEventListener(event, el._debounceHandler);

},

beforeUnmount(el, binding) {

const event = ...; // 需要获取之前绑定的事件类型,可能需要存储

// 这里需要保存event,或者在元素上存储该信息

// 所以在mounted的时候,可能需要在el上存储event和handler

if (el._debounceEvent) {

el.removeEventListener(el._debounceEvent, el._debounceHandler);

el._debounceTimeout = null;

el._debounceHandler = null;

el._debounceEvent = null;

}

}

}

//但这样在beforeUnmount时需要知道之前绑定的事件类型,所以必须在mounted的时候将event存储在el对象上。

所以改进后的代码:

mounted(el, binding) {

// 解析参数

let event = 'input';

let delay = 300;

let handler;

if (typeof binding.value === 'function') {

handler = binding.value;

} else if (typeof binding.value === 'object') {

handler = binding.value.handler;

event = binding.value.event || event;

delay = binding.value.delay || delay;

} else {

console.error('v-debounce指令的值必须为函数或对象');

return;

}

// 保存到el上,以便unmount时使用

el._debounce = {

event,

handler: (...args) => {

clearTimeout(el._debounce.timeout);

el._debounce.timeout = setTimeout(() => {

handler(...args);

}, delay);

},

timeout: null

};

el.addEventListener(event, el._debounce.handler);

},

beforeUnmount(el) {

if (el._debounce) {

el.removeEventListener(el._debounce.event, el._debounce.handler);

clearTimeout(el._debounce.timeout);

el._debounce = null;

}

}

这样,在元素卸载时,可以正确移除事件监听器,并清除定时器。

另外,还需要处理参数中的事件类型和延迟时间。比如用户可能通过指令的参数来指定事件类型,而不是在value对象中。例如:

<input v-debounce:keyup="handler" />

这时候,binding.arg的值是'keyup',可以优先使用arg作为事件类型。

所以需要调整代码,优先使用binding.arg作为事件类型,如果存在的话。

同样,延迟时间也可以通过参数传递,比如:

<input v-debounce:keyup.500="handler" />

但这里的修饰符会被解析为{500: true},这时候可能需要从修饰符中提取数值。例如:

在mounted钩子中:

// 事件类型由arg决定

const event = binding.arg || 'input';

// 延迟时间从修饰符中提取,比如修饰符是500,则delay是500

let delay = 300;

const modifiers = Object.keys(binding.modifiers);

if (modifiers.length > 0) {

const possibleDelay = parseInt(modifiers[0]);

if (!isNaN(possibleDelay)) {

delay = possibleDelay;

}}

或者,用户可以通过value对象传递delay,或者直接传递数值作为delay:

当binding.value是数值时,delay为这个数值,handler需要另外处理?这可能复杂,因为handler必须存在。

所以可能更合理的处理是:

如果binding.arg存在,作为事件类型;

delay可以从修饰符中提取,或者从value对象中提取,或者value为delay数值,同时handler通过其他方式传递?这似乎不太可行。

可能更好的方式是,当用户想要自定义事件类型时,使用参数,delay通过value传递,或者通过修饰符?

这可能比较复杂,所以可能需要重新考虑设计,让用户通过参数和value来传递必要的信息。

例如:

指令的参数是事件类型;

value可以是一个函数(作为handler,delay默认300),或者是一个对象包含handler和delay。

或者,value可以是函数,而delay通过修饰符传递,例如:

点击

此时,binding.arg是'click',修饰符对象是{500: true},所以需要从修饰符中提取延迟时间。

实现的话:

在mounted钩子中:


const event = binding.arg || 'input';

let delay = 300;

// 检查修饰符,寻找可能的数值

const delayModifier = Object.keys(binding.modifiers).find(m => !isNaN(m));

if (delayModifier) {

delay = parseInt(delayModifier);

}

// 或者,如果用户通过value传递delay,则覆盖

if (typeof binding.value === 'object') {

delay = binding.value.delay || delay;

handler = binding.value.handler;

} else if (typeof binding.value === 'function') {

handler = binding.value;

}

这样,用户可以通过修饰符指定延迟时间,或者通过value对象中的delay属性。

但这种方法可能需要处理多个修饰符的情况,比如可能有多个数值,这时候需要决定取哪一个。

或者,如果用户使用类似v-debounce:input.500="handler",则修饰符是500,此时delay为500。

这样,在代码中:

modifiers = Object.keys(binding.modifiers);

if (modifiers.length > 0) {

const possibleDelay = parseInt(modifiers[0]);

if (!isNaN(possibleDelay)) {

delay = possibleDelay;

}

}

这样就能提取出数值型的修饰符作为延迟时间。

这样,用户就可以这样使用指令:

其中,事件类型是input,延迟500ms,处理函数是onInput。

此时,binding.value是onInput函数,binding.arg是input,修饰符是500: true。

这样,在指令中的处理逻辑是:

事件类型是binding.arg || 'input';

delay从修饰符中提取第一个数值,否则默认300;

handler是binding.value;

如果binding.value是一个对象,可能需要其他处理,但在这个例子中,用户传递的是函数,所以需要调整之前的代码,允许处理这种情况。

例如,在mounted钩子中:


const event = binding.arg || 'input';

let delay = 300;

const modifiers = Object.keys(binding.modifiers);

modifiers.forEach(mod => {

const num = parseInt(mod);

if (!isNaN(num)) {

delay = num;

}

});

let handler;

if (typeof binding.value === 'function') {

handler = binding.value;

} else {

console.error('v-debounce指令的值必须是一个函数');

return;

}

然后绑定事件。

这样,用户就可以用v-debounce:事件类型.延迟时间="处理函数"的方式来使用指令。

例如:

<button v-debounce:click.500="handleClick">点击</button>

这样,事件类型是click,延迟500ms,处理函数是handleClick。

这应该比较简洁,用户也容易理解。

那么,最终的指令代码大致如下:


export const debounceDirective = {

mounted(el, binding) {

const event = binding.arg || 'input';

let delay = 300;

const modifiers = Object.keys(binding.modifiers);

// 从修饰符中提取延迟时间

modifiers.forEach(mod => {

const parsed = parseInt(mod);

if (!isNaN(parsed)) {

delay = parsed;

}

});

// 如果binding.value不是函数,报错

if (typeof binding.value !== 'function') {

console.error('v-debounce指令的值必须是一个函数');

return;

}

const handler = binding.value;

// 保存到el上,以便卸载时使用

el._debounce = {

event,

handler: function(...args) {

clearTimeout(el._debounce.timeoutId);

el._debounce.timeoutId = setTimeout(() => {

handler.apply(this, args);

}, delay);

},

timeoutId: null

};

// 添加事件监听

el.addEventListener(event, el._debounce.handler);

},

beforeUnmount(el) {

if (el._debounce) {

el.removeEventListener(el._debounce.event, el._debounce.handler);

clearTimeout(el._debounce.timeoutId);

delete el._debounce;

}

}

};

这样,用户就可以在模板中这样使用:


<template>

<input v-debounce:input.300="handleInput" />

<button v-debounce:click.500="handleClick">点击</button>

</template>

这样的写法比较直观,事件类型通过参数传递,延迟时间通过修饰符(数值)传递,处理函数作为指令的值。

需要注意的是,修饰符在Vue中通常作为键存在,所以例如.500会被视为一个修饰符键名为"500",其值为true。所以上述代码通过遍历修饰符,解析其中的数值,从而得到延迟时间。如果有多个数值修饰符,这里会取最后一个,比如.300.500会取500,但这种情况用户应该避免,应该只传递一个数值修饰符。

此外,如果用户需要更复杂的配置,比如同时指定事件类型和延迟时间,但无法通过参数和修饰符实现,可能需要通过对象语法,但上述方法已经覆盖了大部分常见情况。

综上所述,这个自定义指令的实现应该满足用户的需求,允许通过参数指定事件类型,通过数值修饰符指定延迟时间,处理函数作为指令的值。在元素挂载时绑定事件,并在事件触发时使用防抖函数,同时在组件卸载前清理事件监听器和定时器,避免内存泄漏。

在 Vue 3 中实现自定义防抖指令可以通过以下步骤完成。该指令允许你指定事件类型和延迟时间,并在事件触发时执行防抖处理函数。

完整代码实现

const debounceDirective = {
  mounted(el, binding) {
    // 默认事件类型为 'input',延迟时间 300ms
    const event = binding.arg || 'input';
    let delay = 300;

    // 从修饰符中提取延迟时间(如 .500 表示 500ms)
    const modifiers = Object.keys(binding.modifiers);
    modifiers.forEach(mod => {
      const parsedDelay = parseInt(mod);
      if (!isNaN(parsedDelay)) {
        delay = parsedDelay;
      }
    });

    // 确保绑定值是一个函数
    if (typeof binding.value !== 'function') {
      console.error('v-debounce 必须绑定一个函数');
      return;
    }

    const handler = binding.value;

    // 存储防抖函数及相关参数到元素上
    el._debounce = {
      event,
      handler: (...args) => {
        clearTimeout(el._debounce.timeoutId);
        el._debounce.timeoutId = setTimeout(() => {
          handler(...args);
        }, delay);
      },
      timeoutId: null
    };

    // 添加事件监听
    el.addEventListener(event, el._debounce.handler);
  },
  beforeUnmount(el) {
    // 组件卸载前移除事件监听并清除定时器
    if (el._debounce) {
      el.removeEventListener(el._debounce.event, el._debounce.handler);
      clearTimeout(el._debounce.timeoutId);
      delete el._debounce;
    }
  }
};

// 全局注册指令
app.directive('debounce', debounceDirective);

使用示例

<template>
  <!-- 输入框示例:监听 input 事件,防抖 300ms -->
  <input v-debounce:input="handleInput" />

  <!-- 按钮示例:监听 click 事件,防抖 500ms -->
  <button v-debounce:click.500="handleClick">点击我</button>
</template>

<script setup>
const handleInput = (event) => {
  console.log('防抖后的输入值:', event.target.value);
};

const handleClick = () => {
  console.log('按钮点击,防抖处理');
};
</script>

我们还可以再次的进行封装directive文件

import Highlight from "../help/highlight";// 单独生产一个文件
import Color from "../help/color";
import Debounce from "../help/debounce";
const directives = {  //汇总自定义指令
    // 此处可进行多个指令
    Highlight,//文本高亮指令,
    Color,//文本颜色指令
    Debounce,//防抖指令
  }
  export default {  //导出自定义指令
    install(app) {// 以安装的方式插到app中
     Object.keys(directives).forEach((key) => {    // 遍历directives对象的key
      app.directive(key, directives[key])  // 将每个directive注册到app中
     })
    }
  }
  

然后是main.js 文件

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import directives from './directives/index'
const app = createApp(App)
app.use(directives)// 
app.use(store)
app.use(router)
app.mount('#app')

这个也是一个全局注册的方法

指令说明

  1. 事件类型:通过指令参数指定,如 :input:click,默认是 input
  2. 延迟时间:通过数值修饰符指定(如 .500 表示 500ms),默认 300ms。
  3. 处理函数:绑定到指令的函数,事件触发时执行防抖后的函数。

关键点解释

  • 事件绑定:在 mounted 生命周期钩子中,根据参数和修饰符配置事件监听。
  • 防抖逻辑:每次事件触发时清除之前的定时器,重新设置新的定时器。
  • 内存管理:在 beforeUnmount 钩子中移除事件监听器并清除定时器,防止内存泄漏。
  • 灵活性:支持动态调整事件类型和延迟时间,适应不同场景需求。

通过这种方式,你可以轻松在 Vue 3 项目中复用防抖逻辑,提升交互性能。

如觉得本文对你有帮助的话,欢迎点赞❤❤❤,写作不易,持续输出的背后是无数个日夜的积累,您的点赞是持续写作的动力,感谢支持!有什么写错的,需要增加的可以在评论区留言 谢谢大家支持