【JS 性能】前端性能優化基石:深入理解防抖(Debounce)與節流(Throttle)

【JS 性能】前端性能優化基石:深入理解防抖(Debounce)與節流(Throttle)

所屬專欄: 《前端小技巧集合:讓你的代碼更優雅高效》
上一篇: 【JS 語法】代碼整潔之道:解構賦值與展開語法的 5 個神仙用法
作者: 碼力無邊


? 引言:那一夜,我的頁面因為一次滾動而卡死

嘿,各位熱愛高性能代碼的道友們,我是碼力無邊

在我們的前端江湖中,高性能是一個永恒的追求。我們不斷優化渲染速度,減少網絡請求,壓縮資源包大小。但是,一個再完美的網站,也可能因為幾個微小的操作,瞬間變得卡頓無比,讓用戶體驗直線下降。

回想一下你可能遇到的場景:

  • 用戶在輸入框里快速輸入搜索關鍵詞,每輸入一個字符,你的前端就要觸發一次請求去后端搜索。結果就是:用戶手速越快,頁面越卡,甚至服務器都可能崩潰。
  • 用戶在手機上瘋狂滑動頁面(scroll 事件),或者頻繁拖拽一個元素(mousemove 事件)。這些事件在短時間內被觸發了成百上千次,導致頁面頻繁重繪和回流,最終瀏覽器卡頓、崩潰。
  • 用戶瘋狂點擊一個按鈕(click 事件),導致重復提交表單,或者發送了多次請求。

這些問題,都是因為我們對高頻事件的處理不當。瀏覽器不會憐憫你的 CPU,它會忠實地、毫秒不差地執行你綁定在事件監聽器上的代碼。

那么,如何馴服這些高頻事件,在保證用戶體驗的前提下,減少代碼的執行次數呢?答案就是我們今天的主角——防抖(Debounce)節流(Throttle)

它們就像是兩個智能的“門衛”,負責管理進入你代碼主體的事件流。一個負責“延遲放行”,一個負責“限量放行”。掌握它們,你就能讓你的頁面在處理高頻事件時,依然如絲般順滑,性能爆炸!

一、防抖(Debounce):“你停下來,我就執行”

1.1 核心思想:延遲執行,只執行最后一次

防抖的思路是:當事件連續觸發時,我先不急著執行。我設定一個等待時間,如果在等待時間內事件又被觸發了,我就重新開始計時。只有當事件停止觸發,并且等待時間結束后,我才執行最后一次。

它就像坐地鐵。地鐵關門前,如果有人沖進來,門就會重新打開,等待下一個人沖進來。只有當地鐵站安靜下來一段時間,地鐵才會真正關門開走。

最典型的應用場景:輸入搜索框 (Input/Keyup)

用戶輸入通常是一連串的按鍵,如果我們每按一個鍵都去搜索,會浪費大量資源。我們希望用戶輸入完成后,停頓一下,再去搜索。

1.2 防抖的實現(基礎版)

在 JavaScript 中,我們通常使用 setTimeout 來實現防抖。

/*** 防抖函數 (Debounce)* @param {Function} func - 需要執行的函數* @param {Number} delay - 延遲時間(毫秒)*/
function debounce(func, delay = 500) {let timeoutId = null; // 存儲 setTimeout 的 ID,用于清除計時器return function(...args) {// 1. 在函數執行前,先清除上一次的計時器if (timeoutId) {clearTimeout(timeoutId);}// 2. 重新設置一個新的計時器timeoutId = setTimeout(() => {// 3. 延遲時間到了,執行我們傳入的函數// 注意:使用 apply 或 call 來確保 func 內部的 this 和參數正確傳遞func.apply(this, args);timeoutId = null; // 執行完后可以重置 ID}, delay);};
}

如何使用:

function search(keyword) {console.log(`正在搜索:${keyword}`);// 假設這里是一個實際的后端請求
}// 應用防抖:延遲 300 毫秒
const debounceSearch = debounce(search, 300);// 綁定事件
const searchInput = document.getElementById('search-input');
searchInput.addEventListener('keyup', (event) => {debounceSearch(event.target.value);
});

當你快速敲擊鍵盤時,search() 函數并不會立即執行,只有當你停頓超過 300 毫秒后,它才會執行一次。完美解決了高頻觸發搜索請求的問題。

二、節流(Throttle):“有節奏地執行”

2.1 核心思想:固定周期執行

節流的思路是:在單位時間內(比如 500 毫秒),不管事件觸發了多少次,我只執行一次。

它就像游戲中的技能冷卻。你按下技能鍵后,技能進入冷卻時間,在這個冷卻時間內,你無論怎么按,技能都無法再次釋放,直到冷卻時間結束。

最典型的應用場景:scroll, resize, mousemove

這些事件通常需要高頻地獲取位置信息或更新布局。但我們不需要每毫秒都執行一次,比如,我們只需要每 200 毫秒執行一次就足夠了。

2.2 節流的實現(基礎版)

節流通常使用“時間戳”或“定時器”來實現。這里我們用“時間戳”方式,它更加簡單直接。

/*** 節流函數 (Throttle)* @param {Function} func - 需要執行的函數* @param {Number} interval - 時間間隔(毫秒)*/
function throttle(func, interval = 500) {let lastTime = 0; // 上次執行的時間戳return function(...args) {const now = Date.now(); // 當前時間戳// 1. 判斷時間間隔是否達到if (now - lastTime > interval) {// 2. 執行函數,并更新上次執行時間lastTime = now;func.apply(this, args);}// 3. 如果時間間隔不夠,不做任何事情};
}

如何使用:

function handleScroll() {console.log('滾動事件觸發,正在處理...');// 假設這里是復雜的 DOM 計算或布局更新
}// 應用節流:每 200 毫秒最多執行一次
const throttledScroll = throttle(handleScroll, 200);// 綁定事件
window.addEventListener('scroll', throttledScroll);

無論用戶滾動速度有多快,handleScroll() 函數都只會在每 200 毫秒的固定頻率下執行,極大地減輕了瀏覽器的計算壓力。

三、防抖 vs 節流:我該用哪個?

特性防抖 (Debounce)節流 (Throttle)
執行頻率連續觸發時,只執行最后一次連續觸發時,在指定時間間隔內最多執行一次
等待時間事件停止觸發一段時間后才執行事件開始觸發后,在固定周期內執行
側重場景用戶完成操作,再執行持續操作,需要高頻但有限制地執行
適用場景搜索輸入、窗口調整大小(Resize)頁面滾動(Scroll)、鼠標移動(Mousemove)、高頻點擊

總結:

  • 防抖(Debounce) 強調的是“只在連續操作結束后,執行一次最終的邏輯”。
  • 節流(Throttle) 強調的是“持續操作中,以固定頻率進行執行”。

四、性能考量與注意事項

  1. 清除計時器: 如果使用 debounce,并且你的組件在計時器結束前被銷毀了(比如 Vue/React 組件的卸載),你需要在卸載生命周期里調用 clearTimeout(timeoutId),防止內存泄漏。
  2. event 對象問題: 在使用 debouncethrottle 封裝的函數中,如果你需要訪問原始的 event 對象,要小心。因為在計時器執行時,event 對象可能已經被回收或重用了。通常的做法是,在外部函數中獲取并傳遞你需要的 event 屬性(如 event.target.value),或者在內部函數的參數列表中獲取。在上面的例子中,我們已經使用了 ...argsapply() 來確保參數的傳遞正確性。
  3. 庫的選擇: 如果你的項目足夠大,你可以直接使用成熟的工具庫,如 Lodash 的 _.debounce_.throttle。它們的功能更完善,包含了我們今天沒有涉及的“立即執行”等高級選項。但作為前端工程師,掌握其底層原理,在需要時能夠手寫出來,是必須的修煉。

寫在最后:性能優化,從小處著手

防抖和節流,是前端性能優化中最基礎,但也最有效的“心法”之一。它們能讓你在處理高頻事件時,將 CPU 和內存的壓力降到最低,實現絲般順滑的用戶體驗。

掌握它們,你不僅是解決了眼前的問題,更是養成了一種性能優先的編碼習慣。當你下次再看到一個 scrollmousemove 事件時,你的大腦里就會自動響起警鐘:“嘿,是時候請‘門衛’登場了!”


專欄預告與互動:

我們已經掌握了 JS 基礎和性能優化的要訣。但一個大型項目,除了代碼本身的邏輯,還需要考慮模塊化、依賴管理等工程化問題。

下一篇,我們將探討 JS 模塊化的進階用法——動態 import() 與代碼分割的藝術。我們將學習如何在需要時才加載代碼,有效縮減首屏加載時間,提升用戶體驗!

碼力無邊的修煉之旅,需要你的持續關注!如果你覺得今天的內容讓你功力大增,請點贊、收藏、關注,助我繼續“飛升”!

今日挑戰: 防抖函數的 timeoutId 變量通常定義在外部作用域。如果你的代碼被打包在模塊中,并且有多個地方調用了 debounce 函數,會有問題嗎?在評論區分享你的分析吧!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/95612.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/95612.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/95612.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

線性代數 · 直觀理解矩陣 | 空間變換 / 特征值 / 特征向量

注:本文為 “線性代數 直觀理解矩陣” 相關合輯。 英文引文,機翻未校。 如有內容異常,請看原文。 Understanding matrices intuitively, part 1 直觀理解矩陣(第一部分) 333 March 201120112011 William Gould Intr…

設計模式基礎概念(行為模式):策略模式

概述 策略模式是一種行為設計模式, 它能讓你定義一系列算法, 并將每種算法分別放入獨立的類中, 以使算法的對象能夠相互替換。 主要目的是通過定義相似的算法,替換if else 語句寫法,并且可以隨時相互替換 結構示例 策略…

功能組和功能組狀態的概念關系和區別

在 AUTOSAR Adaptive Platform 中,功能組(Function Group,FG) 和 功能組狀態(Function Group State) 是狀態管理(SM)的核心概念,二者構成靜態邏輯單元與動態行為模式的協…

力扣326:3的冪

力扣326:3的冪題目思路代碼題目 給定一個整數,寫一個函數來判斷它是否是 3 的冪次方。如果是,返回 true ;否則,返回 false 。 整數 n 是 3 的冪次方需滿足:存在整數 x 使得 n 3^x 思路 想要是三的冪次方的話將這個…

前瞻性技術驅動,楓清科技助力制造企業借助大模型完成生產力轉化

麥肯錫于近期發布的《技術趨勢展望2025》更清晰地定義了AI的角色與發展方向。報告在不止一個章節總結了基礎模型加速小型化的趨勢,多模態融合成為主流:企業的模型利用從追求“大而全”轉向“小而精”,高效專用小模型成本降低90%的同時保持性能…

如何遠程連接云服務器上mysql

一:使用系統命令查看端口占用# 查看MySQL進程及其端口sudo netstat -tlnp | grep mysql# 或者使用ss命令sudo ss -tlnp | grep mysql# 查看3306端口(MySQL默認端口)sudo netstat -tlnp | grep 3306出現如下信息,說明端口3306[root…

今日分享:C++模板(全詳解)

😎【博客主頁:你最愛的小傻瓜】😎 🤔【本文內容:C模板 😍 】🤔 -------------------------------------------------------------------------------------------------------------------…

ramdisk內存虛擬盤(一)——前世今生

1990 年代:前因——“硬盤太慢、驅動太多” 背景:早期 Linux 根文件系統要么在軟盤、要么在 IDE 硬盤,內核把對應的軟盤/IDE 驅動編進去即可順利掛載。矛盾出現:隨著 SCSI、PCMCIA、USB、RAID 控制器等百花齊放,如果把…

ETH持續上漲推動DEX熱潮,交易活躍度飆升的XBIT表現強勢出圈

BOSS Wallet 8月15日訊,隨著ETH價格在過去24小時內強勢拉升至4300美元,整個加密市場再度掀起漲勢狂潮,鏈上交易活躍度空前高漲。其中,去中心化交易所平臺迅速成為市場焦點,其平臺活躍度與交易量雙雙上漲,吸…

Stand-In - 輕量級人物一致性視頻生成 高保真視頻人臉交換 ComfyUI工作流 支持50系顯卡 一鍵整合包下載

Stand-In 是一個輕量級、即插即用的身份保護視頻生成框架,只需要上傳一張人物照片,加上一段提示詞,即可生成高度一致性的高保真人物視頻,人臉相似度和自然都幾乎達到100%還原水平。 Stand-In 能把任何一張人臉(甚至動漫…

vue3相關基礎

1、ref和reactive的區別兩者都是響應式數據的聲明。Reactive只適用于非基本數據類型&#xff0c;如對象&#xff0c;數組等。而ref是兼容適用于reactive的的數據類型的以及其他數據&#xff0c;靈活性較高。ref聲明的變量取值時需要.value。在<template></template>…

云手機存儲和本地存儲的區別

云手機存儲通常指云存儲&#xff0c;即數據存儲在云端服務器&#xff0c;本地存儲則是將數據存儲在用戶設備硬件中&#xff0c;主要區別體現在存儲位置、訪問方式、依賴條件等多個方面&#xff0c;具體如下&#xff1a;本地存儲主要是將數據存儲在用戶自有設備的物理硬件中&…

【科研繪圖系列】R語言繪制三維曲線圖

文章目錄 介紹 加載R包 數據下載 導入數據 數據預處理 畫圖 系統信息 參考 介紹 【科研繪圖系列】R語言繪制三維曲線圖 加載R包 library(tidyverse) library(ggsignif) library(RColorBrewer) library(dplyr) library(reshape2) library(grid

python常用包

以下按類別列舉10個常用Python包&#xff0c;并以一句話概括其核心作用&#xff1a; 一、數據分析與科學計算 NumPy&#xff1a;提供高性能多維數組及數學運算&#xff0c;是數值計算的基礎庫。Pandas&#xff1a;通過DataFrame結構實現高效表格數據清洗、分析與處理。SciPy&am…

“ 船新版本 ”

在 GeeLark 最新版本中&#xff0c;增強了 AIGC 生成能力以及 AI 協助自定義任務開發功能&#xff0c;給用戶優化構建從內容生產到運營自動化的完整技術鏈&#xff0c;為跨境電商及企業用戶提供更完善的智能化解決方案&#xff0c;效率翻倍輕松出海。 AIGC 接入 MiniMax-Hailuo…

力扣 —— 二分查找

搜索插入位置 35. 搜索插入位置 - 力扣&#xff08;LeetCode&#xff09; 算法思想&#xff1a; class Solution(object):def searchInsert(self, nums, target):left0 rightlen(nums)-1while left < right :mid (left right) // 2if nums[mid] < target:left mid 1…

USB ADB 簡介

概念 ADB 是 Android 平臺的 調試橋接協議&#xff0c;允許主機&#xff08;PC&#xff09;與 Android 設備通信。 通過 ADB&#xff0c;開發者可以執行命令、調試應用、傳輸文件、訪問 shell、調試 logcat 等。 ADB 運行在 USB 或 TCP/IP 上&#xff0c;但最常用的是 USB 連…

【Golang】:數據類型

目錄 1. 基本數據類型 1.1 布爾類型 1.2 整數類型 1.3 浮點數類型 1.4 復數類型 1.5 字符類型 1.6 字符串類型 2. 類型轉換 2.1 基本數據類型 → string 2.2 string → 基本數據類型 3. 常量 1. 基本數據類型 1.1 布爾類型 Go中的布爾類型取值為true或false&#…

旋鈕鍵盤項目---foc講解(開環)

這里就不過多的講解什么原理&#xff0c;公式的變換了&#xff0c;感興趣的可以看燈哥開源&#xff0c;講解的非常好的。當然&#xff0c;更細致的講解&#xff0c;也可以看b站其他教學。 我這里主要講解我對于開環部分的理解&#xff0c;以及stm32代碼的實現邏輯。可以看作是…

數據科學與計算:爬蟲和數據分析案例筆記

案例 1&#xff1a;中國大學排名爬取與分析 一、任務描述 目標&#xff1a;爬取高三網中國大學排名一覽表&#xff0c;提取學校名稱、總分、全國排名、星級排名、辦學層級等數據&#xff0c;并保存為 CSV 文件。 網址&#xff1a;2021中國的大學排名一覽表_高三網 二、任務…