摘要:自己嘗試一下年在的文章中第一次看到的實現(xiàn)方法。這三種實現(xiàn)方法內(nèi)部不同,但是接口幾乎一致。如你所見,我們使用了參數(shù),因為我們只對用戶停止改變?yōu)g覽器大小時最后一次事件感興趣。
前幾天看到一篇文章,我的公眾號里也分享了《一次發(fā)現(xiàn)underscore源碼bug的經(jīng)歷以及對學(xué)術(shù)界拿來主義的思考》具體文章詳見,微信公眾號:
文中講了大家對throttle和debounce存在誤解,同時提到了《高程3》中實現(xiàn)節(jié)流方法存在一些問題,為了更好的理解這兩個概念,搜了很多相關(guān)文章,詳見文章底部。
throttle與debounce是兩個類似的概念,目的都是隨著時間的推移控制執(zhí)行函數(shù)的次數(shù),但是有些細微的差別。
當(dāng)我們?yōu)镈OM事件關(guān)聯(lián)方法時,若我們有一個debounced和throttled函數(shù)將會很方便,為何?因為這樣我們可以在事件和執(zhí)行函數(shù)之間添加一層控制,注意我們并沒有去控制DOM事件觸發(fā)的次數(shù)。
例如,我們談一下scroll事件,看下面的例子:
See the Pen Scroll events counter by ghostcode (@ghostcode) on CodePen.
當(dāng)你在觸控板或者鼠標滾動時,每次最少會達到30次,在手機上更多??墒悄愕臐L動事件處理函數(shù)對這個頻率是否應(yīng)付的過來?
在2011年,Twitter網(wǎng)站曾爆出一個問題:當(dāng)你在主頁往下滾動時,頁面會變得緩慢以致沒有響應(yīng)。John Resig發(fā)表了一篇文章《 a blog post about the problem》指出直接在scroll事件上面綁定高消耗的事件是一個多么愚蠢的想法。
在那個時候John建議使用一個獨立于scroll事件且每250ms執(zhí)行的輪詢方法。這樣的話處理方法就不會耦合于事件。通過這個簡單的技術(shù),我們可以提高用戶體驗。
現(xiàn)在有一些更先進的事件處理方法,讓我來給你介紹:__Debounce,Throttle和requestAnimationFrame__,同時會介紹一些適用的場景。
Debounce
Debounce技術(shù)使我們可以將一個連續(xù)的調(diào)用歸為一個。
想象你在電梯的場景,當(dāng)電梯門開始要關(guān)閉的時候,突然一個人進來,此時電梯并不會關(guān)閉并且也不會執(zhí)行改變樓層的方法,如果還有人進來同樣的事情會發(fā)生:電梯延遲執(zhí)行它的方法(改變樓層),優(yōu)化了它的資源。
自己嘗試一下,在按鈕上點擊或者移動鼠標:
See the Pen Debounce. Trailing by ghostcode (@ghostcode) on CodePen.
你可以看到快速連續(xù)的事件是如何通過一個debounce事件來表示的。
Leading edge (or "immediate")
你可以發(fā)現(xiàn)事件結(jié)束的時候,debounce的事件并沒有立即執(zhí)行而是等待了一些時間才觸發(fā)。為何不立即觸發(fā),就像開始沒有使用debounce事件處理?直到在連續(xù)執(zhí)行的事件中有一個暫停,才會再次觸發(fā)。
你可以通過一個__leading__的參數(shù)做到:
在underscore.js中,這個參數(shù)叫immediate。
自己嘗試一下:
See the Pen Debounce. Leading by ghostcode (@ghostcode) on CodePen.
Debounce Implementations
2009年在John Hann的文章中第一次看到debounce的實現(xiàn)方法。
在那之后不久,Ben Alman寫了一個jQuery插件(現(xiàn)在不在維護),一年以后Jeremy Ashkenas把此方法添加到underscore.js中,不久又被添加到lodash中。
See the Pen debounce-click by ghostcode (@ghostcode) on CodePen.
這三種實現(xiàn)方法內(nèi)部不同,但是接口幾乎一致。
有段時間underscore采用了Lodash的實現(xiàn)方法,但是在我發(fā)現(xiàn)了一個bug之后,自此兩個庫的實現(xiàn)開始分道揚鑣。
Lodash在_.debounce和_.throttle中添加了許多特性。immediate標示替代了leading和trailing。你可以二選一或者都選,默認情況下,只有trailing是開啟的。
Debounce Examples
當(dāng)改變?yōu)g覽器窗口時,resize事件會觸發(fā)多次。
See the Pen Debounce Resize Event Example by ghostcode (@ghostcode) on CodePen.
如你所見,我們使用了__trailing__參數(shù),因為我們只對用戶停止改變?yōu)g覽器大小時最后一次事件感興趣。
AutoComplete中的Ajax請求使用的keypress
當(dāng)用戶仍舊在輸入的時候,為何每隔50ms發(fā)送Ajax請求?__ _.debounce __可以幫助我們避免額外的工作,只在用戶停止輸入的時候發(fā)送請求。
See the Pen Debouncing keystrokes Example by ghostcode (@ghostcode) on CodePen.
另一個使用場景是在進行input校驗的時候,“你的密碼太短”等類似的信息。
如何使用debounce和throttle以及常見的陷阱?
可以自己實現(xiàn)這兩個方法或者隨便復(fù)制別人blog中的實現(xiàn)方法,我的建議是直接使用underscore和lodash中的方法。如果你只需要這兩個方法,可以定制輸出lodash方法:
npm i -g lodash-cli lodash-cli include=debounce,throttle
一個常見的陷阱:
// WRONG $(window).on("scroll", function() { _.debounce(doSomething, 300); }); // RIGHT $(window).on("scroll", _.debounce(doSomething, 200));
debounce方法賦值給一個變量之后允許我們調(diào)用一個私有方法:__debounced_version.cancel()__:
var debounced_version = _.debounce(doSomething, 200); $(window).on("scroll", debounced_version); // If you need it debounced_version.cancel();
Throttle
使用__ _.throttle __,我們不允許方法在每Xms間執(zhí)行超過一次。
和debounce的主要區(qū)別是throttle保證方法每Xms有規(guī)律的執(zhí)行。
Throttling Examples
一個相當(dāng)常見的例子,用戶在你無限滾動的頁面上向下拖動,你需要判斷現(xiàn)在距離頁面底部多少。如果用戶快接近底部時,我們應(yīng)該發(fā)送請求來加載更多內(nèi)容到頁面。
在此__ _.debounce 沒有用,因為它只會在用戶停止?jié)L動時觸發(fā),但我們需要用戶快到達底部時去請求。通過 _.throttle __我們可以不間斷的監(jiān)測距離底部多遠。
See the Pen Infinite scrolling throttled by ghostcode (@ghostcode) on CodePen.
requestAnimationFrame (rAF)
requestAnimationFrame是另一個頻率限制的方法。
它可以通過__ _.throttle(dosomething, 16)__實現(xiàn),但為了更加精準瀏覽器提供了內(nèi)置API。
我們可以使用rAF API作為throttle方法的替代,考慮一下利弊:
利:
目標60fps(16ms每貞),但是內(nèi)部使用最優(yōu)的時間間隔來渲染
使用簡單并且是標準API,以后不會變動,不需要維護
弊:
rAF的開始或者取消需要我們自己處理,不像.debounce和.throttle內(nèi)部實現(xiàn)
瀏覽器Tag沒有激活,它就不會執(zhí)行
即使多數(shù)現(xiàn)代瀏覽器支持,但是IE9,Opera Mini以及老版本Android依舊不支持。A polyfill到現(xiàn)在依舊需要
rAF在node.js中不支持
根據(jù)經(jīng)驗,我建議在JS執(zhí)行"painting"或"animating"中直接操作屬性和重新計算元素位置時使用rAF。
發(fā)送Ajax請求或者是否添加/刪除class(觸發(fā)一個CSS動畫)時,我會考慮debounce和throttle,此時你可以降低執(zhí)行頻率(200ms而不是16ms)。
rAF的例子
在Paul Lewis的文章激發(fā)下,我只在scroll事件中提供例子。
我一步步的調(diào)throttle到16ms,希望給一個類似的體驗,但是rAF在復(fù)雜場景下或許會提供更好的結(jié)果。
See the Pen Scroll comparison requestAnimationFrame vs throttle by ghostcode (@ghostcode) on CodePen.
一個更好的例子我是在headroom.js中看到的,這里通過一個對象封裝,進行了邏輯解藕。
總結(jié):
使用debounce,throttle和requestAnimationFrame優(yōu)化你的事件處理函數(shù)。每一個方法有一些細微的差別,三個都很有用而且互相彌補。
__debounce:__把突然涌進的事件(鍵盤事件)歸位一個
__throttle:__保證持續(xù)執(zhí)行方法分隔為每Xms執(zhí)行一次。就像每200ms監(jiān)測滾動位置來觸發(fā)css動畫。
__requestAnimationFrame:__throttle的替代方案,當(dāng)你的方法需要重新計算和渲染元素同時你需要更平滑的變動或動畫。注意:IE9- 不支持。
https://blog.coding.net/blog/...
https://css-tricks.com/the-di...
http://stackoverflow.com/ques...
http://demo.nimius.net/deboun...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/79156.html
摘要:可以看下面的栗子這個圖中圖中每個小格大約,右邊有原生事件與節(jié)流去抖插件的與事件。即如果有連續(xù)不斷的觸發(fā),每執(zhí)行一次,用在每隔一定間隔執(zhí)行回調(diào)的場景。執(zhí)行啦打印執(zhí)行啦打印執(zhí)行啦節(jié)流按照上面的說明,節(jié)流就是連續(xù)多次內(nèi)的操作按照指定的間隔來執(zhí)行。 一般在項目中我們會對input、scroll、resize等事件進行節(jié)流控制,防止事件過多觸發(fā),減少資源消耗;在vue的官網(wǎng)的例子中就有關(guān)于lod...
摘要:那么還有最后一個問題,那我之前設(shè)置的定時器怎么辦呢定時器執(zhí)行的是這個函數(shù),而這個函數(shù)又會通過進行一次判斷。 我們在處理事件的時候,有些事件由于觸發(fā)太頻繁,而每次事件都處理的話,會消耗太多資源,導(dǎo)致瀏覽器崩潰。最常見的是我們在移動端實現(xiàn)無限加載的時候,移動端本來滾動就不是很靈敏,如果每次滾動都處理的話,界面就直接卡死了。 因此,我們通常會選擇,不立即處理事件,而是在觸發(fā)一定次數(shù)或一定時間...
摘要:事情是如何發(fā)生的最近干了件事情,發(fā)現(xiàn)了源碼的一個。樓主找到的關(guān)于和區(qū)別的資料如下關(guān)于拿來主義為什么這么多文章里會出現(xiàn)澤卡斯的錯誤代碼樓主想到了一個詞,叫做拿來主義。的文章,就深刻抨擊了拿來主義這一現(xiàn)象。 事情是如何發(fā)生的 最近干了件事情,發(fā)現(xiàn)了 underscore 源碼的一個 bug。這件事本身并沒有什么可說的,但是過程值得我們深思,記錄如下,各位看官仁者見仁智者見智。 平時有瀏覽別...
摘要:一個使用場景某些瀏覽器事件可能會在短時間內(nèi)高頻觸發(fā),比如整窗口大小或滾動頁面。這會導(dǎo)致非常嚴重的性能問題。實現(xiàn)與類似,接收兩個參數(shù),一個是需要截流的函數(shù),另一個是函數(shù)執(zhí)行間隔閾值。 一個使用場景:某些瀏覽器事件可能會在短時間內(nèi)高頻觸發(fā),比如:整窗口大小或滾動頁面。如果給窗口滾動事件添加一個事件監(jiān)聽器,然后用戶不停地快速滾動頁面,那你的事件可能在短短數(shù)秒之內(nèi)被觸發(fā)數(shù)千次。這會導(dǎo)致非常嚴重...
閱讀 1881·2021-11-15 11:39
閱讀 1088·2020-12-03 17:06
閱讀 742·2019-12-27 11:42
閱讀 3277·2019-08-30 13:59
閱讀 1469·2019-08-26 13:22
閱讀 3291·2019-08-26 12:15
閱讀 2479·2019-08-26 10:22
閱讀 1566·2019-08-23 18:40