防抖节流插件throttle-debounce

前言

最近一直在针对 Element-Ui 的源码进行学习,当然重点是学习人家的插件化思想和书写的逻辑性,还有精简(装~)的写法。

发现在源码中使用了 throttle-debounce 这个插件,特意查了一下用法,同时简单分析一下源码。

概念:

节流(Throttle):当事件被连续触发多次时,只有第一次触发会立即执行,后续的触发会根据设定的时间间隔进行限制。

防抖(Debounce):当事件被连续触发多次时,只有最后一次触发会生效,前面的触发将被取消。

使用

直接进行安装:

npm i throttle-debounce --save-dev

throttle方法

含义:固定时间执行回调方法。出现连续调用的函数时,按照特定频率调用函数。

debounce方法

含义:只调用一次回调方法。出现连续调用函数时,最后一次点击结束后,等待特定时间,调用一次函数。

<template>
    <div>
        <el-button type="primary" @click="debounceClick">debounceClick</el-button>
        <el-button type="primary" @click="throttleClick">throttleClick</el-button>
    </div>
</template>

<script>
import { debounce, throttle } from "throttle-debounce";

export default {
    name: 'SelectPage',
    data() {
        return {
            debounceCount: 0,
            throttleCount: 0,
        }
    },
    created() {
        this.refech1 = debounce(1000, () => {
            this.axiosApi();
        });
        this.refech2 = throttle(1000, () => {
            this.axiosApi();
        });
    },
    methods: {
        debounceClick() {
            this.debounceCount++;
            console.log('鼠标点击debounce', this.debounceCount);
            this.refech1();
        },

        throttleClick() {
            this.throttleCount++;
            console.log('鼠标点击throttle', this.throttleCount);
            this.refech2();
        },

        axiosApi() {
            console.log('触发接口');
        },
    }
}
</script>

原理

throttle 源码

/**
 * 限制函数的执行。特别适用于速率限制
 * 处理程序对事件(如调整大小和滚动)的执行。
 *
 * @param {number} delay - 以毫秒为单位,使用零或更大的数值。对于事件回调,推荐值为100或250(甚至更高)。
 * @param {Function} callback - 延迟毫秒后的回调函数。通过this将所有的参数原样执行回调函数。
 * @param {object} [options] - 可选配置。
 * @param {boolean} [options.noTrailing] - 可选, 默认为false。如果noTrailing为true,则回调函数将仅每隔“延迟”毫秒执行一次。如果noTrailing为false,或者不指定,则在最后一次调用后的某个时间节点会执行一次回调函数。也就是说即使不存在新的调用,回调函数仍然会被调用一次。(内部计数器每次都会重置,确保每次调用节流方法都是新的)。
 * @param {boolean} [options.noLeading] - 可选, 默认为false。如果noLeading为false,则第一次调用节流函数时,回调函数将被立即执行。如果noLeading为true则不会立即执行,也就是跳过第一次执行回调函数。
 * 如果noTrailing和noLeading都为 true,则永远不执行回调函数。
 * @param {boolean} [options.debounceMode] - 如果debounceMode为true,则清除内部计时器方法在节流函数开启“延迟”毫秒后执行。如果debounceMode为false,则清除内部计时器的方法在结束节流方法后“延迟”毫秒后执行。 

 * @returns {Function} 返回一个新的、已节流的函数。
 */
export default function (delay, callback, options) {
	// 通过解构获取配置参数
	const {
		noTrailing = false,
		noLeading = false,
		debounceMode = undefined
	} = options || {};
	/*
	 * 在停止调用包装器后,此超时确保在“throttle”和“end”反跳模式下的适当时间执行“callback”。
	 */
	let timeoutID;  // 延迟执行方法记录id
	let cancelled = false;

	// 记录上次执行“回调”的时间。
	let lastExec = 0;

	// 清除现有超时的功能
	// 实际为清除计时器
	function clearExistingTimeout() {
		if (timeoutID) {
			clearTimeout(timeoutID);
		}
	}

	// 用于取消下一次执行的函数
	function cancel(options) {
		const { upcomingOnly = false } = options || {};
		clearExistingTimeout();
		cancelled = !upcomingOnly;
	}

	/*
	 * “包装器”函数封装了所有的节流/去抖动功能,当执行时将限制“回调”的执行速率。
	 */
	function wrapper(...arguments_) {
		let self = this;
		let elapsed = Date.now() - lastExec;

		if (cancelled) {
			return;
		}

		// 执行“callback”并更新“lastExec”时间戳。
		function exec() {
			lastExec = Date.now();
			callback.apply(self, arguments_);
		}

		/*
		 * 如果“debounceMode”为true(在开始时),则用于清除标志以允许将来执行“回调”。
		 */
		function clear() {
			timeoutID = undefined;
		}

		if (!noLeading && debounceMode && !timeoutID) {
			/*
			 * 由于“wrapper”是第一次调用,并且“debounceMode”(在开始时)为true,因此执行 callback 和 noLeading != true。
			 */
			exec();
		}

		clearExistingTimeout();

		if (debounceMode === undefined && elapsed > delay) {
			if (noLeading) {
				/*
				 * 在noLeading的节流模式下,如果超过了“延迟”时间,请更新“lastExec”并计划“callback”在“延迟”ms后执行。
				 */
				lastExec = Date.now();
				if (!noTrailing) {
					timeoutID = setTimeout(debounceMode ? clear : exec, delay);
				}
			} else {
				/*
				 * 没有noLeading的节流模式下,如果超过了“延迟”时间,则立即执行 callback
				 */
				exec();
			}
		} else if (noTrailing !== true) {
			/*
			 * 在noTrailing为false的节流方法中,由于没有超过“延迟”时间,请安排“回调”在最近一次执行后执行“延迟”ms。
			 *
			 * 如果“debounceMode”为true(在开始时),则安排“clear”在“delay”ms之后执行。
			 *
			 * 如果“debounceMode”为false(在结束时),则安排“callback”在“delay”ms之后执行。
			 */
			timeoutID = setTimeout(
				debounceMode ? clear : exec,
				debounceMode === undefined ? delay - elapsed : delay
			);
		}
	}

	wrapper.cancel = cancel;

	// 返回包装器函数
	return wrapper;
}

debounce源码

export default function (delay, callback, options) {
	const { atBegin = false } = options || {};
	return throttle(delay, callback, { debounceMode: atBegin !== false });
}
  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值