摘要:節(jié)流和防抖在開發(fā)項(xiàng)目過(guò)程中很常見(jiàn),例如輸入實(shí)時(shí)搜索滾動(dòng)更新了,等等,大量的場(chǎng)景需要我們對(duì)其進(jìn)行處理。防抖多次觸發(fā),只在最后一次觸發(fā)時(shí),執(zhí)行目標(biāo)函數(shù)。節(jié)流限制目標(biāo)函數(shù)調(diào)用的頻率,比如內(nèi)不能調(diào)用次。
節(jié)流和防抖在開發(fā)項(xiàng)目過(guò)程中很常見(jiàn),例如 input 輸入實(shí)時(shí)搜索、scrollview 滾動(dòng)更新了,等等,大量的場(chǎng)景需要我們對(duì)其進(jìn)行處理。我們由 Lodash 來(lái)介紹,直接進(jìn)入主題吧。
Lodash API
防抖 (debounce) :多次觸發(fā),只在最后一次觸發(fā)時(shí),執(zhí)行目標(biāo)函數(shù)。
lodash.debounce(func, [wait=0], [options={}])
節(jié)流(throttle):限制目標(biāo)函數(shù)調(diào)用的頻率,比如:1s內(nèi)不能調(diào)用2次。
lodash.throttle(func, [wait=0], [options={}])
lodash 在 opitons 參數(shù)中定義了一些選項(xiàng),主要是以下三個(gè):
leading:函數(shù)在每個(gè)等待時(shí)延的開始被調(diào)用,默認(rèn)值為false
trailing:函數(shù)在每個(gè)等待時(shí)延的結(jié)束被調(diào)用,默認(rèn)值是true
maxwait:最大的等待時(shí)間,因?yàn)槿绻?debounce 的函數(shù)調(diào)用時(shí)間不滿足條件,可能永遠(yuǎn)都無(wú)法觸發(fā),因此增加了這個(gè)配置,保證大于一段時(shí)間后一定能執(zhí)行一次函數(shù)
根據(jù) leading 和 trailing 的組合,可以實(shí)現(xiàn)不同的調(diào)用效果:
{leading: true, trailing: false}:只在延時(shí)開始時(shí)調(diào)用
{leading: false, trailing: true}:默認(rèn)情況,即在延時(shí)結(jié)束后才會(huì)調(diào)用函數(shù)
{leading: true, trailing: true}:在延時(shí)開始時(shí)就調(diào)用,延時(shí)結(jié)束后也會(huì)調(diào)用
deboucne 還有 cancel 方法,用于取消防抖動(dòng)調(diào)用
使用
防抖 (debounce):
addEntity = () => { console.log("--------------addEntity---------------") this.debounceFun(); } debounceFun = lodash.debounce(function(e){ console.log("--------------debounceFun---------------"); }, 500,{ leading: true, trailing: false, })
首次點(diǎn)擊時(shí)執(zhí)行,連續(xù)點(diǎn)擊且時(shí)間間隔在500ms之內(nèi),不再執(zhí)行,間隔在500ms之外再次點(diǎn)擊,執(zhí)行。
節(jié)流(throttle):
addEntity = () => { console.log("--------------addEntity---------------"); this.throttleFun(); } throttleFun = lodash.throttle(function(e){ console.log("--------------throttleFun---------------"); }, 500,{ leading: true, trailing: false, })
首次點(diǎn)擊時(shí)執(zhí)行,連續(xù)點(diǎn)擊且間隔在500ms之內(nèi),500ms之后自動(dòng)執(zhí)行一次(注:連續(xù)點(diǎn)擊次數(shù)時(shí)間之后小于500ms,則不會(huì)自動(dòng)執(zhí)行),間隔在500ms之外再次點(diǎn)擊,執(zhí)行。
源碼實(shí)現(xiàn) debounce// 這個(gè)是用來(lái)獲取當(dāng)前時(shí)間戳的 function now() { return +new Date() } /** * 防抖函數(shù),返回函數(shù)連續(xù)調(diào)用時(shí),空閑時(shí)間必須大于或等于 wait,func 才會(huì)執(zhí)行 * * @param {function} func 回調(diào)函數(shù) * @param {number} wait 表示時(shí)間窗口的間隔 * @param {boolean} immediate 設(shè)置為ture時(shí),是否立即調(diào)用函數(shù) * @return {function} 返回客戶調(diào)用函數(shù) */ function debounce (func, wait = 50, immediate = true) { let timer, context, args // 延遲執(zhí)行函數(shù) const later = () => setTimeout(() => { // 延遲函數(shù)執(zhí)行完畢,清空緩存的定時(shí)器序號(hào) timer = null // 延遲執(zhí)行的情況下,函數(shù)會(huì)在延遲函數(shù)中執(zhí)行 // 使用到之前緩存的參數(shù)和上下文 if (!immediate) { func.apply(context, args) context = args = null } }, wait) // 這里返回的函數(shù)是每次實(shí)際調(diào)用的函數(shù) return function(...params) { // 如果沒(méi)有創(chuàng)建延遲執(zhí)行函數(shù)(later),就創(chuàng)建一個(gè) if (!timer) { timer = later() // 如果是立即執(zhí)行,調(diào)用函數(shù) // 否則緩存參數(shù)和調(diào)用上下文 if (immediate) { func.apply(this, params) } else { context = this args = params } // 如果已有延遲執(zhí)行函數(shù)(later),調(diào)用的時(shí)候清除原來(lái)的并重新設(shè)定一個(gè) // 這樣做延遲函數(shù)會(huì)重新計(jì)時(shí) } else { clearTimeout(timer) timer = later() } } }throttle
/** * underscore 節(jié)流函數(shù),返回函數(shù)連續(xù)調(diào)用時(shí),func 執(zhí)行頻率限定為 次 / wait * * @param {function} func 回調(diào)函數(shù) * @param {number} wait 表示時(shí)間窗口的間隔 * @param {object} options 如果想忽略開始函數(shù)的的調(diào)用,傳入{leading: false}。 * 如果想忽略結(jié)尾函數(shù)的調(diào)用,傳入{trailing: false} * 兩者不能共存,否則函數(shù)不能執(zhí)行 * @return {function} 返回客戶調(diào)用函數(shù) */ _.throttle = function(func, wait, options) { var context, args, result; var timeout = null; // 之前的時(shí)間戳 var previous = 0; // 如果 options 沒(méi)傳則設(shè)為空對(duì)象 if (!options) options = {}; // 定時(shí)器回調(diào)函數(shù) var later = function() { // 如果設(shè)置了 leading,就將 previous 設(shè)為 0 // 用于下面函數(shù)的第一個(gè) if 判斷 previous = options.leading === false ? 0 : _.now(); // 置空一是為了防止內(nèi)存泄漏,二是為了下面的定時(shí)器判斷 timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; return function() { // 獲得當(dāng)前時(shí)間戳 var now = _.now(); // 首次進(jìn)入前者肯定為 true // 如果需要第一次不執(zhí)行函數(shù) // 就將上次時(shí)間戳設(shè)為當(dāng)前的 // 這樣在接下來(lái)計(jì)算 remaining 的值時(shí)會(huì)大于0 if (!previous && options.leading === false) previous = now; // 計(jì)算剩余時(shí)間 var remaining = wait - (now - previous); context = this; args = arguments; // 如果當(dāng)前調(diào)用已經(jīng)大于上次調(diào)用時(shí)間 + wait // 或者用戶手動(dòng)調(diào)了時(shí)間 // 如果設(shè)置了 trailing,只會(huì)進(jìn)入這個(gè)條件 // 如果沒(méi)有設(shè)置 leading,那么第一次會(huì)進(jìn)入這個(gè)條件 // 還有一點(diǎn),你可能會(huì)覺(jué)得開啟了定時(shí)器那么應(yīng)該不會(huì)進(jìn)入這個(gè) if 條件了 // 其實(shí)還是會(huì)進(jìn)入的,因?yàn)槎〞r(shí)器的延時(shí) // 并不是準(zhǔn)確的時(shí)間,很可能你設(shè)置了2秒 // 但是他需要2.2秒才觸發(fā),這時(shí)候就會(huì)進(jìn)入這個(gè)條件 if (remaining <= 0 || remaining > wait) { // 如果存在定時(shí)器就清理掉否則會(huì)調(diào)用二次回調(diào) if (timeout) { clearTimeout(timeout); timeout = null; } previous = now; result = func.apply(context, args); if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { // 判斷是否設(shè)置了定時(shí)器和 trailing // 沒(méi)有的話就開啟一個(gè)定時(shí)器 // 并且不能不能同時(shí)設(shè)置 leading 和 trailing timeout = setTimeout(later, remaining); } return result; }; };
走在最后,歡迎 star:https://github.com/sisterAn/blog
歡迎關(guān)注:前端瓶子君
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/105925.html
摘要:節(jié)流節(jié)流限制了一個(gè)函數(shù)可以在短時(shí)間內(nèi)被調(diào)用的次數(shù)。更新防抖防抖確保了一個(gè)函數(shù)只有在一個(gè)固定時(shí)間段內(nèi)沒(méi)有被調(diào)用過(guò)后,才會(huì)再次被調(diào)用。再換句話說(shuō)防抖會(huì)等待事件不再高頻發(fā)生,再觸發(fā)。這個(gè)網(wǎng)站很好的可視化了節(jié)流與防抖。 節(jié)流 Throttling 節(jié)流限制了一個(gè)函數(shù)可以在短時(shí)間內(nèi)被調(diào)用的次數(shù)。可以這樣形容:在一毫秒內(nèi)最多執(zhí)行此函數(shù) 1 次。 Throttling enforces a maxi...
摘要:個(gè)人博客原文地址背景我們?cè)陂_發(fā)的過(guò)程中會(huì)經(jīng)常使用如等事件,如果正常綁定事件處理函數(shù)的話,有可能在很短的時(shí)間內(nèi)多次連續(xù)觸發(fā)事件,十分影響性能。 個(gè)人博客原文地址 背景 我們?cè)陂_發(fā)的過(guò)程中會(huì)經(jīng)常使用如scroll、resize、touchmove等事件,如果正常綁定事件處理函數(shù)的話,有可能在很短的時(shí)間內(nèi)多次連續(xù)觸發(fā)事件,十分影響性能。因此針對(duì)這類事件要進(jìn)行節(jié)流或者防抖處理 節(jié)流 節(jié)流的意思...
摘要:首先重置防抖函數(shù)最后調(diào)用時(shí)間,然后去觸發(fā)一個(gè)定時(shí)器,保證后接下來(lái)的執(zhí)行。這就避免了手動(dòng)管理定時(shí)器。 ??之前遇到過(guò)一個(gè)場(chǎng)景,頁(yè)面上有幾個(gè)d3.js繪制的圖形。如果調(diào)整瀏覽器可視區(qū)大小,會(huì)引發(fā)圖形重繪。當(dāng)圖中的節(jié)點(diǎn)比較多的時(shí)候,頁(yè)面會(huì)顯得異??D。為了限制類似于這種短時(shí)間內(nèi)高頻率觸發(fā)的情況,我們可以使用防抖函數(shù)。 ??實(shí)際開發(fā)過(guò)程中,這樣的情況其實(shí)很多,比如: 頁(yè)面的scroll事件 ...
摘要:您的支持是我最大的動(dòng)力,我會(huì)保證提供高質(zhì)與清晰的文章與您共同成長(zhǎng)。一些文章中的與上面所談到的設(shè)置類似。防抖防抖技術(shù)允許我們捆綁多個(gè)連續(xù)調(diào)用成為單一的一次調(diào)用。防抖的應(yīng)用這個(gè)簡(jiǎn)單的舉個(gè) 歡迎star和watch我的github issue blog,歡迎加入討論。您的支持是我最大的動(dòng)力,我會(huì)保證提供高質(zhì)與清晰的文章與您共同成長(zhǎng)。 節(jié)流[throttle]與防抖[debounce]在前...
摘要:節(jié)流分流,與防抖去抖實(shí)現(xiàn)原理相似。本文主要討論節(jié)流,鏡像文章防抖理解,實(shí)踐與實(shí)現(xiàn)。分開討論防抖和節(jié)流,主要是為了讓一些還不太了解節(jié)流防抖的讀者能夠有針對(duì)性地,逐一掌握它們。上方為未節(jié)流模式,每一次觸發(fā)都會(huì)繪制一個(gè)圓點(diǎn)。 showImg(https://segmentfault.com/img/bVbqMwN?w=1280&h=720); 節(jié)流(分流),與防抖(去抖)實(shí)現(xiàn)原理相似。本文主...
閱讀 2066·2021-09-22 15:43
閱讀 8724·2021-09-22 15:07
閱讀 1083·2021-09-03 10:28
閱讀 2057·2021-08-19 10:57
閱讀 1065·2020-01-08 12:18
閱讀 2977·2019-08-29 15:09
閱讀 1527·2019-08-29 14:05
閱讀 1645·2019-08-29 13:57