別再瘋狂調(diào)用你的 API 了!學(xué)會防抖與節(jié)流
幾個月前,我在寫一個搜索框 —— 那種“邊輸入邊搜索”的實時提示功能。
一開始一切順利,直到我飛快打了幾個字,后臺日志就像加班焦慮的實習(xí)生一樣開始狂吼:
“5 秒內(nèi)處理了 400 次請求?!?/p>
我愣住了。
隊友悠悠地來了一句:
“哥們,是時候冷靜一下了?!?/p>
那一刻我意識到:
是的,我聽說過“防抖”(debounce),也聽說過“節(jié)流”(throttle)。
但它們到底怎么用,什么時候用,我內(nèi)心一直歸到那個抽屜——“以后有空一定搞懂?!?/p>
那么,現(xiàn)在就來真正搞懂它們
我終于把防抖與節(jié)流整明白了。
這篇文章不會跟你講一堆術(shù)語,而是從實際開發(fā)需求出發(fā),告訴你它們的本質(zhì)差異和應(yīng)用場景。
防抖 vs 節(jié)流,到底有啥區(qū)別?
一句話總結(jié):
- 防抖(debounce):等你停下來,我再執(zhí)行
- 節(jié)流(throttle):規(guī)定時間內(nèi)只能執(zhí)行一次
聽起來像是兄弟倆,但性格完全不同。
Debounce:等等...再等等...
場景: 用戶在搜索框輸入內(nèi)容,你希望只在他停下 300ms 后再發(fā)出請求。
function debounce(fn, delay) {
let timer
return (...args) => {
clearTimeout(timer)
timer = setTimeout(() => fn(...args), delay)
}
}
適合用在:
- 搜索框輸入(不打完不請求)
- 輸入自動保存(暫停一下才保存)
- 頁面 resize(用戶調(diào)整完再處理)
邏輯關(guān)鍵詞:每次觸發(fā)都會重置計時器,只有“沉默”一段時間后才執(zhí)行。
Throttle:給我一點節(jié)奏感
場景: 用戶在瘋狂滾動頁面,你想追蹤他的行為,但沒必要每一像素都記錄一次。
function throttle(fn, limit) {
let lastTime = 0
return (...args) => {
const now = Date.now()
if (now - lastTime >= limit) {
lastTime = now
fn(...args)
}
}
}
適合用在:
- 頁面滾動事件
- 鼠標(biāo)移動追蹤
- resize 實時反饋(希望“過程中”就執(zhí)行)
邏輯關(guān)鍵詞:固定頻率執(zhí)行,即使觸發(fā)頻率再高也“控頻”。
該選哪個?
目標(biāo) | 使用方式 |
用戶操作結(jié)束后再執(zhí)行 | ? 使用防抖 debounce |
用戶操作過程中定時執(zhí)行 | ? 使用節(jié)流 throttle |
真正的場景:不是 MDN 抄過來就完事了
你可能會遇到這樣的情況:
- 一個搜索建議接口,用戶打字太快,結(jié)果請求太多(→ 用防抖)
- 頁面滾動時觸發(fā)組件動畫或懶加載(→ 用節(jié)流)
- 表單自動保存草稿,用戶停頓 1 秒后提交(→ 用防抖)
- 追蹤用戶鼠標(biāo)軌跡但別搞崩瀏覽器(→ 用節(jié)流)
BONUS:Lodash 了解一下
實話實說,大部分項目里你都有 lodash,那還費什么勁?
import { debounce, throttle } from 'lodash'
- 想防抖就
debounce(fn, 300)
- 想節(jié)流就
throttle(fn, 200)
別忘了在 React 組件里 清理定時器:
useEffect(() => {
const handler = debounce(() => { /* do something */ }, 300)
return () => handler.cancel()
}, [])
最后的建議:別讓你的函數(shù)失控!
很多開發(fā)者在加交互效果時,忽略了性能成本:
- 鍵盤事件、滾動事件、窗口變化,這些都可能每秒觸發(fā)上百次
- 忽視控制,輕則頁面卡頓,重則服務(wù)器被打爆
了解防抖與節(jié)流,拯救了我的 API 賬單、頁面性能和代碼審查時的自尊。
所以如果你以前也裝懂過“debounce vs throttle”,別擔(dān)心,我懂你。
現(xiàn)在開始,防抖請耐心,節(jié)流請克制。
別再讓函數(shù)亂跑,掌控它們,才是真正的前端高手。