理解
防抖:n秒内没有再次触发,则执行最后一次触发的事件
节流:n秒内多次触发只执行第一次,n秒后可再次执行,可以减少执行的次数
使用场景:
防抖:输入框搜索
节流:页面滚动,窗口大小调整,多次点击
ts装饰器
// 防抖
export function PromiseDebounce(time = 200, delayTime = 400) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const original = descriptor.value;
const debounceFn = function () {
let timer = null;
const execFn = function (...args: any) {
if (timer) clearTimeout(timer);
timer = setTimeout(async () => {
const data = await Promise.all([
// @ts-ignore
original.call(this, ...args),
new Promise((resolve) => {
setTimeout(resolve, time);
}),
]);
return data[0];
}, time);
};
return execFn;
};
descriptor.value = debounceFn();
return descriptor;
};
}
/**
* 网络请求节流装饰器,对网络请求进行锁操作,网络请求结束之后释放
* @param time 等网络请求结束和设置时间完成之后释放节流
* @param delayTime 延迟被装饰方法的触发时间
*/
export function PromiseThrottle(time = 200, delayTime = 500) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const original = descriptor.value;
const throttleFn = function () {
let status = '';
const execFn = async function (...args: any) {
if (status === 'ing') {
return;
}
status = 'ing';
try {
const data = await Promise.all([
// @ts-ignore
original.call(this, ...args),
new Promise((resolve) => {
setTimeout(resolve, time);
}),
]);
return data[0];
} finally {
setTimeout(() => {
status = '';
}, delayTime);
}
};
return execFn;
};
descriptor.value = throttleFn();
return descriptor;
};
}
// 使用
@PromiseThrottle(1000)
private funA() {}
vue2指令
// /directive/index.js
const customDirective = [
{
name: 'common-throttle',
config: {
/**
* button节流指令 v-common-throttle:[事件名称]="[时间]"
* v-common-throttle:click="3000"
*/
inserted(el, binding) {
const { arg: eventType = 'click', value = 1000 } = binding;
el.addEventListener(eventType, () => {
if (!el.disabled) {
el.disabled = true;
el.__commonThrottleTimer__ = setTimeout(() => {
el.disabled = false;
el.__commonThrottleTimer__ = null;
}, value);
}
});
},
unbind(el) {
el.__commonThrottleTimer__ && clearTimeout(el.__commonThrottleTimer__);
}
}
},
{
name: 'common-debounce',
config: {
/**
* button 防抖指令 v-common-debounce:[事件名称].[延迟时间]="[函数名]"
* v-common-debounce:click.500="myfunction"
* v-common-debounce:click.500="() => myfunction(val1, val2)"
*/
bind(el, binding) {
const eventName = binding.arg || 'click';
const [, delay = 300] = binding.rawName.split('.');
el.addEventListener(eventName, (e) => {
el.__commonDebounceTimer__ && clearTimer(el.__commonDebounceTimer__);
el.__commonDebounceTimer__ = setTimeout(() => {
if (typeof binding.value === 'function') {
binding.value(e);
}
clearTimer(el.__commonDebounceTimer__);
}, delay);
});
},
unbind(el) {
clearTimer(el.__commonDebounceTimer__);
}
}
}
];
export default function (Vue) {
customDirective.forEach((item) => {
const { name, config } = item;
Vue.directive(name, config);
});
}
// main.js
import registDirective from '@/directive/index.js';
registDirective(Vue);
通用方法
// /utils/index.js
/**
* 防抖函数
* @param {Function} fn - 执行的函数
* @param {Number} wait - 时间,单位毫秒
*/
export function debounce(fn, wait) {
let timer;
return () => {
clearTimeout(timer);
timer = setTimeout(() => {
fn();
}, wait);
};
}
/**
* 节流函数
* @param {Function} fn - 执行的函数
* @param {Number} wait - 时间,单位毫秒
*/
export function throttle(fn, wait) {
let interval;
return function () {
if (interval) {
return;
}
fn(arguments);
interval = setTimeout(() => {
clearInterval(interval);
interval = null;
}, wait);
};
}
// index.vue
@input="debounceChangeValue"
import { debounce } from '@/utils/index.js';
created() {
this.debounceChangeValue = debounce(() => {
this.changeValue();
}, 1000);
}
有一个细节,clearInterval之后还给interval置为null,这是因为单纯的清除定时器,interval还是有值的,if(interval)就会为true。
function test() {
const interval = setInterval(() => {
console.log(1);
}, 1000);
// 4s之后清除定时器,并查看interval的值
setTimeout(() => {
clearInterval(interval);
if (interval) {
console.log('interval', interval);
}
}, 4000);
}