一种新思路且便捷和兼容几乎全部前端框架的防抖/节流方法

仅作为记录,方便未来的自己(记录思路)

本来在写着vue3,想通过添加一个自定义指令在不修改原有@click的情况下去实现防抖/节流,本想着让ai来写,结果ChatGPT和DeepSeek写的都有问题,大致思路就是通过拦截替换原有的事件,俩ai都想通过el._vei.onClick来获取原有事件,奈何我前段时间更新了vue3版本,结果这个事件换位置了,导致我测试时一直失败,查找资料发现这种在3.3.4以及以下是有效的

//3.3.4以及以下是有效
const fn = el._vei.onClick;

3.3.5及以后发生了变化,需要通过以下代码获取

// 3.3.5及以后有效
const vei_key = Object.getOwnPropertySymbols(el)?.[0];
const fn = el[vei_key].onClick;

只能自己上手了,我换了一个思路,为什么要去拦截原有事件,把原有事件的执行时机交给防抖/节流中去执行(还需要去操心this指向问题),我只需要在不该执行的时候去阻止这次执行即可,由此思路写出了下面的代码(代码可能不够完善,只提供个思路)

debounce-click.js文件:

// debounce-click.js

const name = 'debounce-click';

const directive = {
  mounted(el, binding) {
    const delay = binding.value ?? 300;
    let timer = null;

    const debouncedHandler = (event) => {
      // 如果是模拟出来的合成事件则不去执行stopImmediatePropagation,让流程正常走完
      if (event.isSynthetic) return;
      
      // 拦截用户手动触发的原始点击事件
      event.stopImmediatePropagation();
      
      // 每次点击都重置定时器
      if (timer) clearTimeout(timer);
      timer = setTimeout(() => {
        timer = null;
        // 派发一个合成的事件
        const newEvent = new event.constructor(event.type, event);
        newEvent.isSynthetic = true;
        el.dispatchEvent(newEvent);
      }, delay);
    };

    el.addEventListener('click', debouncedHandler, true);
    el._debouncedHandler = debouncedHandler;
  },

  beforeUnmount(el) {
    if (el._debouncedHandler) {
      el.removeEventListener('click', el._debouncedHandler, true);
      delete el._debouncedHandler;
    }
  }
};

export default {
  name,
  directive
};

throttle-click.js文件:

// throttle-click.js

const name = 'throttle-click';

const directive = {
	mounted(el, binding) {
		const delay = binding.value ?? 300;
		let isDisabled = false;

		const throttledHandler = (event) => {
			if (isDisabled) return event.stopImmediatePropagation();
			isDisabled = true;
			setTimeout(() => isDisabled = false, delay);
		};

		el._throttledHandler = throttledHandler;
		el.addEventListener('click', throttledHandler, true);
	},

	beforeUnmount(el) {
		if (el._throttledHandler) {
			el.removeEventListener('click', el._throttledHandler);
			delete el._throttledHandler;
		}
	}
};

export default {
	name,
	directive
};

注册指令(vue3)

这里我是方便在vue中去注册自定义指令所以把代码写成了这种结构,如果是其他前端框架直接手动调用directive的mounted和beforeUnmount也是可以正常运行达到预期效果的(传递个dom元素和传递和时间即可)

// index.js 提供一个统一注册的方法出去

import debounceClick from './debounce-click.js';
import throttleClick from './throttle-click.js';

export default function registerDirectives(app) {
	const directives = [debounceClick, throttleClick];
	directives.forEach(directive => {
		app.directive(directive.name, directive.directive);
	});
}


// main.js

import registerDirectives from '@/directives'; 
const app = createApp(App);
// 批量注册自定义指令
registerDirectives(app);

最后就可以这样子使用了

// vue

<div v-debounce-click @click="onClick" />

<div v-throttle-click @click="onClick" />

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值