在前端開發(fā)中,我們經(jīng)常需要處理高頻事件(如輸入框輸入、滾動(dòng)、窗口調(diào)整大小等)。如果不加限制,瀏覽器會(huì)頻繁觸發(fā)回調(diào)函數(shù),導(dǎo)致性能問題,甚至頁面卡頓。
防抖(Debounce) 和 節(jié)流(Throttle) 是兩種優(yōu)化方案,可以有效控制事件觸發(fā)的頻率,提高應(yīng)用的性能和用戶體驗(yàn)。
本篇文章將詳細(xì)解析 防抖和節(jié)流的原理、適用場景及代碼實(shí)現(xiàn),幫助你更好地優(yōu)化前端應(yīng)用。
1. 什么是防抖(Debounce)?
防抖是一種在事件觸發(fā)后延遲執(zhí)行的技術(shù),如果在延遲期間事件被再次觸發(fā),計(jì)時(shí)器會(huì)重置,重新計(jì)算延遲時(shí)間。
核心思想:短時(shí)間內(nèi)多次觸發(fā),只執(zhí)行最后一次。
適用場景
搜索框輸入(防止用戶每次輸入都發(fā)送請求)
窗口調(diào)整大小(resize)(防止短時(shí)間內(nèi)多次觸發(fā)計(jì)算)
表單輸入驗(yàn)證(用戶停止輸入后再進(jìn)行驗(yàn)證)
function debounce(fn, delay = 300) {
let timer;
return function (...args) {
if (timer) clearTimeout(timer); // 清除之前的定時(shí)器
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
示例:輸入框防抖
<input type="text" id="search" placeholder="請輸入內(nèi)容">
<script>
const input = document.getElementById('search');
input.addEventListener('input', debounce((e) => {
console.log('搜索內(nèi)容:', e.target.value);
}, 500));
</script>
效果:用戶輸入停止 500ms 后,才觸發(fā) console.log。
2. 什么是節(jié)流(Throttle)?
節(jié)流是一種限定函數(shù)執(zhí)行頻率的技術(shù),即在一定時(shí)間間隔內(nèi),函數(shù)最多執(zhí)行一次,即使事件被頻繁觸發(fā)。
核心思想:高頻觸發(fā),固定間隔執(zhí)行。
適用場景
滾動(dòng)事件(scroll)(如懶加載、頁面滾動(dòng)監(jiān)聽)
鼠標(biāo)移動(dòng)(mousemove)(防止觸發(fā)過多計(jì)算)
按鈕點(diǎn)擊(click)(防止用戶瘋狂點(diǎn)擊)
代碼實(shí)現(xiàn)
function throttle(fn, interval = 300) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= interval) {
fn.apply(this, args);
lastTime = now;
}
};
}
示例:滾動(dòng)監(jiān)聽
<div style="height: 2000px;"></div>
<script>
window.addEventListener('scroll', throttle(() => {
console.log('滾動(dòng)中...', new Date().toLocaleTimeString());
}, 1000));
</script>
效果:無論滾動(dòng)多快,scroll 事件每秒最多執(zhí)行一次。
3. 防抖 vs. 節(jié)流:有什么區(qū)別?
防抖(Debounce)
觸發(fā)方式:事件觸發(fā)后延遲執(zhí)行
特性:短時(shí)間連續(xù)觸發(fā),只執(zhí)行最后一次
適用場景:輸入框輸入、搜索框、表單驗(yàn)證
優(yōu)勢:避免無效調(diào)用,減少資源消耗
實(shí)現(xiàn)方式:setTimeout 延遲執(zhí)行
節(jié)流(Throttle)
觸發(fā)方式:事件觸發(fā)后間隔執(zhí)行
特性:固定時(shí)間間隔內(nèi)最多執(zhí)行一次
適用場景:滾動(dòng)、鼠標(biāo)移動(dòng)、按鈕點(diǎn)擊
優(yōu)勢:保證高頻事件可執(zhí)行,提高流暢度
實(shí)現(xiàn)方式:Date.now() 控制執(zhí)行間隔
總結(jié)
如果你想等用戶停止操作后再執(zhí)行任務(wù),使用防抖(Debounce)。
如果你希望控制函數(shù)執(zhí)行的頻率,使用節(jié)流(Throttle)。
4. 進(jìn)階優(yōu)化:使用 Lodash
我們可以直接使用 Lodash 提供的 debounce 和 throttle 方法,避免自己實(shí)現(xiàn)。
npm install lodash
Lodash 防抖
import _ from 'lodash';
const handleInput = _.debounce((e) => {
console.log('防抖觸發(fā):', e.target.value);
}, 500);
document.getElementById('search').addEventListener('input', handleInput);
Lodash 節(jié)流
const handleScroll = _.throttle(() => {
console.log('節(jié)流觸發(fā)', new Date().toLocaleTimeString());
}, 1000);
window.addEventListener('scroll', handleScroll);
Lodash 優(yōu)勢:內(nèi)部優(yōu)化更好,支持立即執(zhí)行(leading)和延遲執(zhí)行(trailing)。
結(jié)合 Vue 實(shí)戰(zhàn)應(yīng)用
//Vue 中使用防抖
<template>
<input v-model="searchText" @input="handleInput" placeholder="搜索">
</template>
<script>
import { ref } from 'vue';
import _ from 'lodash';
export default {
setup() {
const searchText = ref('');
const handleInput = _.debounce((e) => {
console.log('搜索關(guān)鍵詞:', e.target.value);
}, 500);
return { searchText, handleInput };
},
};
</script>
//效果:用戶停止輸入 500ms 后才會(huì)觸發(fā)搜索請求。
//Vue 中使用節(jié)流
<template>
<button @click="handleClick">點(diǎn)擊節(jié)流</button>
</template>
<script>
import _ from 'lodash';
export default {
setup() {
const handleClick = _.throttle(() => {
console.log('按鈕點(diǎn)擊', new Date().toLocaleTimeString());
}, 2000);
return { handleClick };
},
};
</script>
//效果:按鈕每 2s 只能點(diǎn)擊一次。