今天在改一個看似很簡單以前也經常遇到的一個bug發現了很多問題。實際結果肯定沒有想象的那麼簡單。所以我想總結一下:保證下次不會踩坑。 業務場景是點擊一個按鈕,會產生一個彈框。重覆快速的點擊多次,會產生多個彈框。那麼這個問題該怎麼解決呢? 1. 函數防抖節流 這兩個東西我不知道看過了多少文檔,可到現在 ...
今天在改一個看似很簡單以前也經常遇到的一個bug發現了很多問題。
實際結果肯定沒有想象的那麼簡單。所以我想總結一下:保證下次不會踩坑。
---
業務場景是點擊一個按鈕,會產生一個彈框。重覆快速的點擊多次,會產生多個彈框。
那麼這個問題該怎麼解決呢?
1. 函數防抖節流
這兩個東西我不知道看過了多少文檔,可到現在掌握的還是不夠好。
所以今天我想趁熱打鐵一波,把他們徹徹底底給搞明白!
防抖:什麼是防抖呢? 就是比如你規定一段延時 時間,在這段時間內你持續觸發這個事件,事件處理函數才會執行一次,如果設定的時間到來之前,你又觸發了一次事件,就重新開始延時。 來個圖更加形象!
//接著肯定就是實現防抖的代碼了 //防抖 function debounce(fn,wait) { var timeout = null; return function() { if(timeout !== null) clearTimeout(timeout) timeout = setTimeout(fn,wait) } } //處理函數 function handle() { console.log(Math.random()) } //滾動事件 window.addEventListener('scroll',debounce(handle,1000))
當持續觸發scroll事件時,事件處理函數handle只在停止滾動1000毫秒之後才會調用一次,也就是說在持續觸發scroll事件的過程中,事件處理函數handle一直沒有執行。
節流:那麼什麼是節流呢?認識了防抖之後一定不要混淆了節流的概念,我之前就是犯這個錯誤。
言歸正傳,節流就是當你持續觸發事件時,保證一段時間內只調用一次事件的處理函數,通俗點說就比如我們水龍頭放水,閥門一打開,水嘩嘩的往下流,秉著勤儉節約的優良傳統美德,我們要把水龍頭關小點,最好是如我們心意按照一定規律在某個時間間隔內一滴一滴的往下滴。如下圖,持續觸發scroll事件時,並不立即執行handle函數,每隔1000毫秒才會執行一次handle函數。
函數節流主要有兩種實現方法:時間戳和定時器。接下來分別用兩種方法實現throttle~
//節流函數(時間戳) function throttle(fn, delay) { //記錄第一次執行時的時間 var prev = Date.now() return function() { var contxt = this var args = arguments var now = Date.now() //如果當前時間減去上一次執行的時間大於等於延時時間 if(now -prev >= delay) { //執行 fn.apply(contxt, args) //執行後的時間 prev = Date.now() } } } //處理函數 function handle() { console.log(Math.random()) } //滾動事件 window.addEventListener('scroll',throttle(handle,1000)) ``` ```javascript //節流函數(定時器) function throttle(fn, delay) { var timer = null return function() { var contxt = this var args = arguments //如果定時器不存在 if(!timer) { timer = setTimeout(function(){ fn.apply(contxt,args) timer = null },delay) } } } //處理函數 function handle() { console.log(Math.random()) } //滾動事件 window.addEventListener('scroll',throttle(handle,1000))
當觸發事件的時候,我們設置一個定時器,再次觸發事件的時候,如果定時器存在,就不執行,直到delay時間後,定時器執行執行函數,並且清空定時器,這樣就可以設置下個定時器。當第一次觸發事件時,不會立即執行函數,而是在delay秒後才執行。而後再怎麼頻繁觸發事件,也都是每delay時間才執行一次。當最後一次停止觸發後,由於定時器的delay延遲,可能還會執行一次函數。
節流中用時間戳或定時器都是可以的。更精確地,可以用時間戳+定時器,當第一次觸發事件時馬上執行事件處理函數,最後一次觸發事件後也還會執行一次事件處理函數。
// 節流throttle代碼(時間戳+定時器): var throttle = function(func, delay) { var timer = null; var startTime = Date.now(); return function() { var curTime = Date.now(); var remaining = delay - (curTime - startTime); var context = this; var args = arguments; clearTimeout(timer); if (remaining <= 0) { func.apply(context, args); startTime = Date.now(); } else { timer = setTimeout(func, remaining); } } } function handle() { console.log(Math.random()); } window.addEventListener('scroll', throttle(handle, 1000));
總結:
函數防抖:將幾次操作合併為一此操作進行。原理是維護一個計時器,規定在delay時間後觸發函數,但是在delay時間內再次觸發的話,就會取消之前的計時器而重新設置。這樣一來,只有最後一次操作能被觸發。
函數節流:使得一定時間內只觸發一次函數。原理是通過判斷是否到達一定時間來觸發函數。
區別: 函數節流不管事件觸發有多頻繁,都會保證在規定時間內一定會執行一次真正的事件處理函數,而函數防抖只是在最後一次事件後才觸發一次函數。 比如在頁面的無限載入場景下,我們需要用戶在滾動頁面時,每隔一段時間發一次 Ajax 請求,而不是在用戶停下滾動頁面操作時才去請求數據。這樣的場景,就適合用節流技術來實現。
今天本想用其中一種方法解決我前面提到的業務場景,可萬萬沒想到的是,兩種方法我試了,都不行,可能是微信小程式的機制和PC端不同的緣故吧,所以最後 我採用了遮罩層的方法實現。
就是在他第一次點擊的時候就設置一個遮罩層,不讓他點擊了,不就成功解決了這個問題了麽???