前端函數(shù)防抖實(shí)現(xiàn)完整實(shí)例詳解
前言
在現(xiàn)代 Web 應(yīng)用中,函數(shù)防抖(debounce)是一種常見且高效的性能優(yōu)化手段,用于限制高頻事件觸發(fā)下的函數(shù)調(diào)用次數(shù),從而減少不必要的計(jì)算、網(wǎng)絡(luò)請求或 DOM 操作。本文將從“為什么使用防抖”切入,介紹典型的應(yīng)用場景,深入解析防抖原理,并給出從零實(shí)現(xiàn)到在實(shí)際項(xiàng)目中使用 Lodash 的完整代碼示例,幫助你快速掌握前端防抖技術(shù)。
為什么使用防抖
函數(shù)防抖的核心思想是在連續(xù)觸發(fā)的事件停止后,僅執(zhí)行最后一次調(diào)用,以避免頻繁觸發(fā)帶來的性能問題 ([MDN Web Docs][1])。
在不使用防抖的情況下,例如在 input
輸入事件或 window.resize
事件中直接調(diào)用邏輯,頁面可能會因短時(shí)間內(nèi)大量調(diào)用而出現(xiàn)卡頓或請求風(fēng)暴 ([GeeksforGeeks][2])。
通過防抖,可以讓函數(shù)在用戶停止輸入、滾動或調(diào)整窗口大小后的一定延遲內(nèi)才執(zhí)行,極大提高資源利用效率并提升用戶體驗(yàn) ([Medium][3])。
函數(shù)防抖的應(yīng)用場景
- 輸入框?qū)崟r(shí)搜索建議
在用戶輸入關(guān)鍵詞時(shí)觸發(fā)搜索接口,若不加限制,每次keyup
都會發(fā)起請求,極易導(dǎo)致接口壓力過大。使用防抖后,只在用戶停止輸入(如 300ms)后才發(fā)送請求,有效降低調(diào)用次數(shù) ([自由代碼營][4])。 - 按鈕防連點(diǎn)
對于提交表單或支付按鈕,連續(xù)點(diǎn)擊可能導(dǎo)致多次提交。給點(diǎn)擊事件綁定防抖函數(shù),可在用戶短時(shí)間內(nèi)多次點(diǎn)擊時(shí)只執(zhí)行一次提交操作 ([DEV Community][5])。 - 窗口大小調(diào)整(resize)
當(dāng)頁面布局需根據(jù)窗口大小實(shí)時(shí)計(jì)算或重繪時(shí),resize
事件會頻繁觸發(fā),添加防抖能減少重繪次數(shù),提升性能 ([Medium][6])。 - 滾動監(jiān)聽
結(jié)合無限滾動或懶加載,當(dāng)用戶滾動頁面時(shí)應(yīng)控制數(shù)據(jù)加載頻率,避免重復(fù)請求或過度渲染 ([Medium][3])。
函數(shù)防抖原理與手寫實(shí)現(xiàn)
原理
防抖函數(shù)通過內(nèi)部維護(hù)一個(gè)定時(shí)器 ID,每次調(diào)用時(shí)先清除之前的定時(shí)器,再啟動一個(gè)新的延遲執(zhí)行定時(shí)器;只有在最后一次調(diào)用后的延遲時(shí)間到達(dá)后,才真正執(zhí)行目標(biāo)函數(shù) ([GeeksforGeeks][2], [Gist][7])。
手寫實(shí)現(xiàn)
/** * 簡易版防抖函數(shù) * @param {Function} func - 需要防抖的函數(shù) * @param {number} wait - 延遲時(shí)間(毫秒) * @returns {Function} - 防抖后返回的新函數(shù) */ function debounce(func, wait) { let timeoutId; // 聲明定時(shí)器 ID return function(...args) { // 返回一個(gè)閉包函數(shù) clearTimeout(timeoutId); // 清除上一次定時(shí)器 timeoutId = setTimeout(() => { // 啟動新的定時(shí)器 func.apply(this, args); // 延遲執(zhí)行目標(biāo)函數(shù) }, wait); }; }
上述代碼利用 JavaScript 閉包,讓每個(gè)防抖函數(shù)維護(hù)獨(dú)立的 timeoutId
,在多次調(diào)用時(shí)只有最后一次延遲結(jié)束后觸發(fā) ([Stack Overflow][8])。
使用 Lodash 的 _.debounce
在實(shí)際項(xiàng)目中,為了減少手寫錯(cuò)誤并獲得更豐富的功能(如 leading
、trailing
、cancel
、flush
等選項(xiàng)),推薦使用成熟的工具庫 Lodash 的 _.debounce
方法 ([Lodash][9])。
# 安裝 lodash.debounce 子模塊 npm install lodash.debounce
import debounce from 'lodash.debounce'; /** * 在搜索框中使用防抖 * 當(dāng)用戶停止輸入 300ms 后才觸發(fā)搜索 */ const searchInput = document.getElementById('search'); function onSearch(query) { // 發(fā)送搜索請求 console.log('搜索關(guān)鍵詞:', query); } const debouncedSearch = debounce(onSearch, 300, { leading: false, trailing: true }); searchInput.addEventListener('input', (e) => { debouncedSearch(e.target.value); });
leading
: 是否在延遲開始前調(diào)用一次,默認(rèn)false
。trailing
: 是否在延遲結(jié)束后調(diào)用一次,默認(rèn)true
。- 返回的函數(shù)還擁有
cancel()
和flush()
方法,可在需要時(shí)取消或立即執(zhí)行待定調(diào)用 ([GeeksforGeeks][10])。
完整示例:防抖搜索組件
下面給出一個(gè)完整的示例,包括 HTML、樣式與 JavaScript 代碼,你可以直接復(fù)制運(yùn)行:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Debounce Demo</title> <style> body { font-family: sans-serif; padding: 2rem; } #results { margin-top: 1rem; } .item { padding: 0.5rem 0; border-bottom: 1px solid #eee; } </style> </head> <body> <h1>Debounce 搜索示例</h1> <input id="search" type="text" placeholder="輸入關(guān)鍵詞…" autocomplete="off" /> <div id="results"></div> <script type="module"> import debounce from 'lodash.debounce'; const search = document.getElementById('search'); const results = document.getElementById('results'); // 模擬異步搜索函數(shù) async function fetchResults(query) { // 假數(shù)據(jù) return ['蘋果', '香蕉', '橘子', '西瓜'] .filter(item => item.includes(query)); } async function handleSearch(query) { const list = await fetchResults(query); results.innerHTML = list.map(item => `<div class="item">${item}</div>`).join(''); } // 300ms 防抖,禁止 leading,允許 trailing const debouncedHandle = debounce(handleSearch, 300, { leading: false }); search.addEventListener('input', e => { const q = e.target.value.trim(); if (q) debouncedHandle(q); else results.innerHTML = ''; }); </script> </body> </html>
結(jié)語
函數(shù)防抖是前端性能優(yōu)化中的一項(xiàng)基礎(chǔ)技術(shù),適用于各種需要限制高頻事件調(diào)用的場景,通過簡單的定時(shí)器邏輯或成熟的 Lodash 工具庫,就能快速落地。掌握防抖和其“兄弟”節(jié)流(throttle),能讓你的應(yīng)用在面對頻繁用戶交互時(shí)依然保持流暢、穩(wěn)定。歡迎在項(xiàng)目中實(shí)踐并根據(jù)業(yè)務(wù)需求調(diào)整參數(shù),實(shí)現(xiàn)更靈活的性能優(yōu)化。
到此這篇關(guān)于前端函數(shù)防抖實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)前端函數(shù)防抖實(shí)現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript實(shí)現(xiàn)彈性導(dǎo)航效果
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)彈性導(dǎo)航效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11微信小程序picker組件關(guān)于objectArray數(shù)據(jù)類型的綁定方法
這篇文章主要介紹了微信小程序picker組件關(guān)于objectArray數(shù)據(jù)類型的綁定方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-03-03javascript中如何快速獲取數(shù)組最后一個(gè)值
這篇文章主要介紹了javascript中如何快速獲取數(shù)組最后一個(gè)值問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01webpack DllPlugin xxx is not defined解決辦法
這篇文章主要介紹了webpack DllPlugin xxx is not defined解決辦法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12利用純JS實(shí)現(xiàn)JSON轉(zhuǎn)CSV的技巧全攻略
在數(shù)據(jù)處理和導(dǎo)出方面,JSON 和 CSV 格式是兩個(gè)常見的角色,今天,我們將介紹如何在純 JavaScript 中,利用開源庫 @json2csv/plainjs 輕松實(shí)現(xiàn)從 JSON 轉(zhuǎn)換到 CSV,幫助你高效處理數(shù)據(jù),需要的朋友可以參考下2025-06-06Eclipse編輯jsp、js文件時(shí)卡死現(xiàn)象的解決辦法匯總
使用Eclipse編輯jsp、js文件時(shí),經(jīng)常出現(xiàn)卡死現(xiàn)象,在網(wǎng)上百度了N次,經(jīng)過N次優(yōu)化調(diào)整后,卡死現(xiàn)象逐步好轉(zhuǎn),下面通過腳本之家平臺給大家分享幾種解決辦法,需要的朋友參考下2016-02-02