一棋返、背景
防抖和節(jié)流是兩種不同的控制一個函數(shù)執(zhí)行次數(shù)的方法母债,其目的都是為了節(jié)約計算機資源午磁。
當我們操作DOM的時候,加上節(jié)流或者防抖就非常有必要毡们,因為眾所周知迅皇,操作DOM的開銷是非常大的,所以要盡可能減少DOM操作次數(shù)衙熔。
看下面的在線例子:
https://codepen.io/dcorb/pen/PZOZgB
當鼠標滾動或者拖拽的時候可以輕易地每秒觸發(fā)30個事件登颓,而且在移動端慢速滾動可以達到每秒100次,要是這么短的時間內每次都觸發(fā)一個或多個函數(shù)红氯,那瀏覽器應該會卡死崩潰的框咙,所以這就引出了防抖和節(jié)流。
二痢甘、防抖(Debounce)
防抖允許我們把多次連續(xù)的調用放到一次里面去執(zhí)行喇嘱,在事件被觸發(fā)n秒后再執(zhí)行回調,如果在這n秒內又被觸發(fā)塞栅,則重新計時者铜。
試想一下,你正身處一部電梯之中放椰,電梯門開始關閉作烟,然后突然又來了一個人,按下了開門鍵砾医,電梯門開了拿撩,他順利上了電梯,然后電梯又開始緩慢關閉如蚜,但是此時又匆匆跑來了另一個人压恒,電梯門又開了,此時错邦,快要遲到的你心里面開始有點不滿了难咕。但是五督,這其實就是一個防抖的實際例子稀蟋,電梯盡可能的使其資源利用率達到最高乘盖,在盡可能的情況下,載上盡可能多的人。
下面是一個例子:
https://codepen.io/dcorb/pen/KVxGqN
- 具體實現(xiàn)
function debounce(func,delay) {
let timer;
return function(...args) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
func.apply(this,arguments);
},delay)
}
}
Lodash中實現(xiàn) _.debounce
和 _.throttle
的功能很全面讨勤,可以直接使用箭跳,其中的 throttle
函數(shù)是使用 _.debounce
新增 maxWait` 選項來實現(xiàn)的, 有興趣可以自行查看 源碼。
- Resize實例
// Based on http://www.paulirish.com/2009/throttled-smartresize-jquery-event-handler/
$(document).ready(function(){
var $win = $(window);
var $left_panel = $('.left-panel');
var $right_panel = $('.right-panel');
function display_info($div) {
$div.append($win.width() + ' x ' + $win.height() + '<br>');
}
$(window).on('resize', function(){
display_info($left_panel);
});
$(window).on('resize', _.debounce(function() {
display_info($right_panel);
}, 400));
});
可以用來監(jiān)聽瀏覽器窗口尺寸變化事件潭千。
https://codepen.io/dcorb/pen/XXPjpd
- 輸入驗證實例
$(document).ready(function(){
var $statusKey = $('.status-key');
var $statusAjax = $('.status-ajax');
var intervalId;
// Fake ajax request. Just for demo
function make_ajax_request(e){
var that = this;
$statusAjax.html('That\'s enough waiting. Making now the ajax request');
intervalId = setTimeout(function(){
$statusKey.html('Type here. I will detect when you stop typing');
$statusAjax.html('');
$(that).val(''); // empty field
},2000);
}
// Event handlers to show information when events are being emitted
$('.autocomplete')
.on('keydown', function (){
$statusKey.html('Waiting for more keystrokes... ');
clearInterval(intervalId);
})
// Display when the ajax request will happen (after user stops typing)
// Exagerated value of 1.2 seconds for demo purposes, but in a real example would be better from 50ms to 200ms
$('.autocomplete').on('keydown',
_.debounce(make_ajax_request, 1300));
});
當用戶輸入并且發(fā)起Ajax
請求的時候谱姓,_.debounce
可以用來實現(xiàn)防抖,比如間隔2秒未檢測到用戶輸入才發(fā)起請求刨晴。
三屉来、節(jié)流(Throttle)
- 具體實現(xiàn)
使用定時器
var throttle = function(func, delay) {
var timer = null;
return function() {
if (!timer) {
timer = setTimeout(function() {
func.apply(this,arguments);
timer = null;
},delay);
}
}
}
另一種定時器寫法:
let isAllow = true;
function throttle() {
let fun = function() {
if (!isAllow)
return;
let timer = setTimeout(() => {
console.log("throttle");
clearTimeout(timer);
timer = null;
isAllow = true;
},1000);
};
fun();
}
二者的區(qū)別是后者使用了isAllow標志位來判斷是否需要執(zhí)行函數(shù)狈癞。
- 滾動條實例
一個十分常見的例子茄靠,當用戶的鼠標滾動的時候,檢查鼠標位置離底部的距離蝶桶,當接近底部時慨绳,發(fā)起Ajax
請求。
https://codepen.io/dcorb/pen/eJLMxa
requestAnimationFrame
requestAnimationFrame
是另一種限制函數(shù)執(zhí)行次數(shù)的方式真竖,可以認為與_.throttle
類似脐雪,但是其擁有更高的準確度,因為其本身就是為了更好的精確度而生的原生API恢共。
requestAnimationFrame的優(yōu)點
- 致力于60fps(16ms每幀)战秋,自己可以決定最好的渲染時間。
- 簡單且標準的原生API讨韭,不太容易改變脂信,減少維護成本。
requestAnimationFrame的缺點
- 需要手動開始或取消rAFs拐袜,
.debounce
或.throttle
則不需要吉嚣。 - 如果tab沒有激活梢薪,則不會執(zhí)行蹬铺,即使觸發(fā) Ascroll, mouse 或者 keyboard 等事件也不行.
- 不支持 IE9, Opera Mini 和 老版本 Android.
- 不支持
node.js
,所以不能在服務端使用秉撇。
requestAnimationFrame實例
與_.throttle
相比甜攀,同時設置 16ms, 相同的性能環(huán)境下琐馆,rAF 可以在更復雜的情況下?lián)碛懈玫慕Y果规阀。
https://codepen.io/dcorb/pen/pgOKKw
四、結論
一般來說瘦麸,如果你的JavaScript函數(shù)是直接繪制或者動畫某些屬性谁撼,那么可以使用
requestAnimationFrame
,在需要重新計算元素位置的地方使用滋饲。需要發(fā)起
Ajax
請求厉碟,或者決定是否增加或刪除一個類的時候喊巍,可以考慮使用_.debounce
或者_.throttle
。debounce: 在事件被觸發(fā)x秒后再執(zhí)行回調箍鼓,如果在這x秒內又被觸發(fā)崭参,則重新計時。
throttle: 規(guī)定在一個單位時間內款咖,只能觸發(fā)一次函數(shù)何暮。如果這個單位時間內觸發(fā)多次函數(shù),只有一次生效铐殃。
requestAnimationFrame: 節(jié)流的另外一種選項海洼,在函數(shù)重新計算或者渲染元素時,需要平滑過渡或者動畫的時候使用富腊。
五贰军、參考
https://css-tricks.com/debouncing-throttling-explained-examples/