简介
什么是防抖
防抖(Debouncing)是一种前端开发中常用的技术,用于限制函数的执行频率。在防抖的机制下,一个函数在一定时间内只会执行一次,即使它被频繁地调用。
具体来说,防抖的实现方式是设置一个定时器,在每次函数被调用时,先清除之前的定时器,然后重新设置一个新的定时器。如果在设定的时间间隔内没有再次调用该函数,定时器触发,函数才会被执行。这样可以确保函数不会在短时间内被频繁执行,从而减轻性能压力和提高用户体验。
防抖的应用场景包括输入框输入、滚动事件、窗口大小改变等需要控制执行频率的操作。在这些场景下,防抖可以避免不必要的函数执行,节省资源和提高页面响应速度。
什么是节流
节流(Throttling)是一种前端开发中常用的技术,用于控制函数的执行频率。与防抖不同,节流是确保函数在一定时间内最多只执行一次,而不是在时间内只执行一次。换句话说,函数在设定的时间间隔内最多执行一次,无论它被调用了多少次。
节流的实现方式通常是使用定时器。当函数被调用时,先检查当前是否已经存在定时器,如果存在,则不执行函数;如果不存在,则设置一个定时器,在定时器到达时执行函数。这样可以确保函数不会在短时间内被频繁执行,从而减轻性能压力。
节流的应用场景包括滚动事件、拖拽事件、鼠标移动等需要限制执行频率的操作。通过节流,可以平滑地控制这些事件的触发频率,避免不必要的计算和操作,提高页面性能和用户体验。
为什么要进行节流和防抖
节流(Throttle
)和防抖(Debounce
)对于前端开发人员来说应该是十分熟悉的,节流(Throttle
)和防抖(Debounce
)是两种可以节省性能的编程技术,两者的目的都是为了优化性能,提高用户体验,都是基于 DOM 事件限制正在执行的 JavaScript 数量的方法。但两者的有什么不一样呢?
节流会强制执行一个函数在一段时间内可以被调用的最大次数。如“最多每 100 毫秒执行一次此函数”。
假设在正常情况下,会在 10
秒内调用此函数 1,000
次。如果将其限制为每 100
毫秒仅一次,则该函数最多只会执行 100
次。
(10s * 1,000)
=10,000 ms
10,000 ms / 100 ms
限制 =100
个最大调用
防抖强制一个函数在一段时间内没有被调用之前不会被再次调用。如“仅当 100 毫秒过去了而没有被调用时才执行此函数”。
也许一个函数在短时间内被调用 1000
次,分散在3秒内,然后停止调用。如果在 100
毫秒的时间内启动,这个功能只会在 3.1
秒的时间内启动一次。每次在突发事件期间调用该函数时,它都会重置恢复计时器。
节流与防抖区别是什么?
这些概念的一个主要用例是某些 DOM
事件,例如滚动和调整大小。例如,如果将滚动处理程序附加到一个元素,并将该元素向下滚动 5000
像素,可能会看到 100
多个事件被触发。如果事件处理程序做了大量工作(例如繁重的计算和其他 DOM
操作),可能会看到性能问题(卡顿)。如果可以减少执行该处理程序的次数,而不会对经验造成太大影响,那么这可能是值得的。
常用的场景如下
- 等到用户停止调整窗口大小
- 在用户停止输入之前不要触发
AJAX
事件 - 监测或者获取页面的滚动位置,最多每
100ms
响应一次 - 在应用中拖动元素时确保良好的性能
VUE中大的节流throttle和防抖debounce
基本原理
在Vue中,防抖和节流的基本原理都是利用定时器和函数的闭包来控制函数的执行频率。下面分别介绍防抖和节流在Vue中的基本原理:
防抖(Debouncing)
- 定时器机制: 当触发事件时,防抖会清除之前的定时器,然后重新设置一个新的定时器。
- 函数闭包: 防抖利用函数的闭包保存定时器的状态,确保每次调用函数都能操作同一个定时器。
- 延迟执行: 如果在设定的时间间隔内没有再次触发事件,定时器触发,函数执行。
在Vue中,可以通过以下代码实现简单的防抖:
javascriptCopy code// 防抖函数
function debounce(func, delay) {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
// 使用防抖函数
const debouncedFunction = debounce(myFunction, 300);
节流(Throttling)
- 定时器机制: 节流也使用定时器,但与防抖不同,它是在每次触发事件时先检查是否存在定时器,如果存在则不执行函数,如果不存在则设置一个新的定时器。
- 函数闭包: 节流同样利用函数的闭包保存定时器的状态,确保每次调用函数都能操作同一个定时器。
- 限制执行频率: 函数在设定的时间间隔内最多执行一次,即使触发多次也不会重复执行。
在Vue中,可以通过以下代码实现简单的节流:
javascriptCopy code// 节流函数
function throttle(func, delay) {
let timer;
return function (...args) {
if (!timer) {
func.apply(this, args);
timer = setTimeout(() => {
timer = null;
}, delay);
}
};
}
// 使用节流函数
const throttledFunction = throttle(myFunction, 300);
这些基本原理可以帮助理解在Vue中如何通过定时器和函数闭包实现防抖和节流,以及如何控制函数的执行频率。
安装
npm install throttle-debounce --save
throttle
限制回调函数的执行频率
/**
* 节流(限制函数的执行频率)
* @param delay 延迟的时间
* @param noTrailing 在最后一次调用时是否执行 callback,true 不执行,false 执行
* @param callback 目标回调函数
* @param debounceMode
*/
throttle(delay, noTrailing, callback, debounceMode)
dobounceMode: 为 true 时,在被调用时,先执行 callback,在没有被调用时,在指定的延迟之后执行 clear,如果在clear 执行之前继续调用,会重置定时器;为 false 时,在被调用时,不会执行 callback,在指定的延迟之后执行 callback,如果在 callback 执行之前继续调用,会重置定时器
debounce
限制回掉函数的执行频率,但是不同于 debounce 的是,debounce 能保证在一系列调用的时间内,回调函数只执行一次
atBegin: 为 true 时,在被调用时,会马上执行 callback,如果在延迟时间之前继续调用,不会执行 callback;为 false 时,在被调用时,不会执行 callback,在延迟时间之后会执行 callback,如果在延迟时间之前继续调用,会重置定时器
Lodash进行节流与防抖
节流(Throttle
)场景
$("body").on("scroll", _.throttle(function() {
// 处理逻辑
}, 100));
防抖(Debounce
)场景
实际生活中,如百度搜索,输入文本后会出现下拉选择,这个过程一般绑定文本事件 keypress
。
下图描述了使用防抖(Debounce
)前状态的性能监控捕获,每次 keypress
引发事件时,它都会触发搜索引擎请求数据并将结果呈现在屏幕上。事实上,这些结果并没有被用户看到,因为它们已经被最新 keypress
事件的后续结果覆盖了,屏幕上仅呈现最新结果。
$(window).on("resize", _.debounce(function() {
// 处理逻辑
}, 100));
防抖和节流的参数和效果调优
防抖函数和节流函数的参数解析
防抖函数和节流函数都包含两个主要参数:被控制的函数(通常称为目标函数)和时间间隔。下面对这两个参数进行解析:
防抖函数参数解析:
- 目标函数(func): 这是你希望限制执行频率的实际函数。防抖会延迟执行这个函数,确保在设定的时间间隔内只执行一次。
- 时间间隔(delay): 这是设定的延迟时间,即触发事件后延迟多久再执行目标函数。单位通常是毫秒。
javascriptCopy codefunction debounce(func, delay) {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
// 使用防抖函数
const debouncedFunction = debounce(myFunction, 300);
节流函数参数解析:
- 目标函数(func): 同样是被限制执行频率的实际函数。节流确保在设定的时间间隔内最多执行一次该函数。
- 时间间隔(delay): 这是设定的时间间隔,即触发事件后每隔多久执行一次目标函数。单位通常是毫秒。
javascriptCopy codefunction throttle(func, delay) {
let timer;
return function (...args) {
if (!timer) {
func.apply(this, args);
timer = setTimeout(() => {
timer = null;
}, delay);
}
};
}
// 使用节流函数
const throttledFunction = throttle(myFunction, 300);
这些参数允许你在使用防抖和节流时根据需要调整目标函数的执行频率和延迟时间,以满足特定的应用场景。
如何调整防抖和节流的时间间隔
调整防抖和节流的时间间隔是根据实际需求和应用场景来确定的。时间间隔决定了在触发事件后多久执行一次目标函数。以下是如何调整防抖和节流的时间间隔:
调整防抖的时间间隔:
在防抖函数中,时间间隔是通过设定的延迟时间(delay)来控制的。你可以通过修改 debounce
函数的调用,传入不同的延迟时间来调整防抖的时间间隔。
javascriptCopy code// 使用不同的延迟时间调整防抖的时间间隔
const debouncedFunction = debounce(myFunction, 500); // 延迟500毫秒
调整节流的时间间隔:
在节流函数中,时间间隔也是通过设定的延迟时间(delay)来控制的。你可以通过修改 throttle
函数的调用,传入不同的延迟时间来调整节流的时间间隔。
javascriptCopy code// 使用不同的延迟时间调整节流的时间间隔
const throttledFunction = throttle(myFunction, 1000); // 每隔1000毫秒执行一次
根据实际场景的需要,你可以灵活地选择不同的时间间隔。较短的时间间隔可以提高响应速度,但可能增加函数执行的频率;较长的时间间隔则可以减轻性能压力,但可能降低响应速度。根据具体要求和性能考虑,选择一个合适的时间间隔是很重要的。
防抖和节流的效果和性能考虑
防抖的效果和性能考虑:
- 效果: 防抖的主要效果是确保在一定时间内只执行一次目标函数。这对于减少函数的执行频率、避免不必要的计算和请求非常有效。常用于输入框输入、滚动事件等需要延迟触发的场景。
- 性能: 防抖在一段时间内只执行一次函数,因此可以减轻浏览器的负担,提高页面的性能。然而,在需要即时响应的场景中,过长的延迟时间可能导致用户感知到的延迟。
节流的效果和性能考虑:
- 效果: 节流的主要效果是确保在设定的时间间隔内最多执行一次目标函数。这有助于平滑控制函数的执行频率,适用于一些需要稳定触发的场景,如滚动事件、鼠标移动等。
- 性能: 节流同样可以减轻浏览器负担,提高性能。由于函数有一定的执行频率,即使在短时间内触发多次,也只会执行一次。这有助于减少不必要的计算和请求。
性能对比:
-
防抖 vs.节流: 防抖和节流都有助于提高性能,但在不同场景下的选择取决于实际需求。防抖适用于一定时间内只需执行一次的场景,而节流适用于需要平滑控制执行频率的场景。
-
延迟时间: 调整防抖和节流的延迟时间是关键,过长的延迟可能导致用户感知到的延迟,而过短的延迟可能增加函数执行频率,影响性能。
防抖和节流的注意事项
防抖和节流可能带来的副作用
防抖可能带来的副作用:
- 延迟执行: 防抖的本质是延迟执行函数,因此在一段时间内函数可能不会立即执行。这可能导致用户在交互中感知到延迟,特别是在需要即时响应的情况下。
- 频繁触发导致多次执行: 如果防抖的延迟时间设置得很短,而函数被频繁触发,可能导致函数在短时间内多次执行,反而失去了防抖的效果。
节流可能带来的副作用:
- 执行间隔固定: 节流确保在设定的时间间隔内最多执行一次函数,这可能导致函数的执行频率固定,不管事件触发的频率如何。在某些场景下,这可能不符合实际需求。
- 可能有延迟: 节流会在设定的时间间隔内执行一次函数,因此可能在一些需要即时响应的场景中引入一定的延迟。
- 不适用于所有场景: 节流适用于一些需要平滑控制执行频率的场景,但在一些需要严格同步的场景下可能不合适,如某些动画效果。
其他注意事项:
- 忽略事件信息: 防抖和节流在处理事件时,有可能忽略了一些事件信息。如果函数的执行需要关键的事件信息,需要谨慎使用防抖和节流。
- 用户体验影响: 过度使用防抖和节流可能影响用户体验,特别是在一些需要即时响应的交互场景中。
如何避免滥用防抖和节流
避免滥用防抖和节流是非常重要的,以确保它们在实际应用中发挥正确的作用。以下是一些建议:
- 根据实际需求选择: 防抖和节流是为了解决特定问题而设计的工具,而不是为了在所有地方都使用。在使用之前,确保理解它们的作用和适用场景,仅在有必要的情况下使用。
- 考虑用户体验: 在一些需要即时响应的交互场景中,过度使用防抖和节流可能导致用户感知到的延迟。在这些场景中,需要权衡性能和用户体验,选择适当的策略。
- 谨慎设置延迟时间: 防抖和节流的效果受到延迟时间的影响。设置过短的延迟可能导致函数频繁执行,失去优化效果;设置过长的延迟可能导致用户感知到的延迟。因此,需要谨慎选择和调整延迟时间。
- 避免过度嵌套: 避免在函数之间过度嵌套防抖或节流,以免造成复杂性和难以维护的代码。清晰地定义每个函数的作用和调用关系。
- 注意函数参数: 如果目标函数依赖于事件参数或其他重要信息,请确保防抖和节流的实现不会丢失或影响这些信息。可能需要适当地调整函数签名。
- 进行测试和性能优化: 在使用防抖和节流之前,进行充分的测试,特别是在性能方面。确保它们确实带来了预期的性能提升,并在必要时进行优化。
- 文档和团队共识: 在团队中建立对防抖和节流的共识,并在代码中提供清晰的文档。确保团队成员理解何时使用以及如何正确使用这些技术。
最佳实践和建议
节流的最佳实践:
- 理解场景: 节流适用于需要在设定的时间间隔内最多执行一次函数的场景,例如滚动事件、鼠标移动等。
- 合理设置延迟: 根据实际需求合理设置节流的延迟时间,避免设置过短或过长的延迟。
- 平滑控制频率: 节流有助于平滑控制函数的执行频率,确保在设定的时间间隔内最多执行一次。
- 适用于稳定触发场景: 节流适用于一些需要稳定触发的场景,避免函数执行频率波动过大。
其他建议:
- 测试和性能优化: 在使用防抖和节流之前进行充分的测试,确保它们带来了预期的性能提升,并在必要时进行优化。
- 清晰文档和团队共识: 提供清晰的文档,确保团队成员理解何时使用以及如何正确使用防抖和节流。建立团队共识,以避免滥用。
- 适当的应用: 防抖和节流并非适用于所有场景,根据实际需求适当应用,避免在不必要的地方引入复杂性。
- 结合使用: 在一些场景中,结合使用防抖和节流,根据具体需求进行灵活调整。
- 考虑移动端兼容性: 在移动端开发中,由于触摸事件的特性,需要谨慎使用防抖和节流,确保在不同设备上有良好的兼容性。
通过理解场景、合理设置延迟、测试性能以及建立清晰的文档和共识,可以确保防抖和节流在代码中得到正确的应用,提高性能并改善用户体验。