一、什么是防抖 debounce
防抖是一种控制函数执行频率的函数,它的主要目的是延迟函数的执行,直到事件停止触发一段时间后才执行。防抖通常应用于那些用户快速频繁操作的场景中,例如搜索框的输入,防止用户每输入一个字符就触发一次请求。
工作原理
当事件发生时,防抖会重新设置计时器,如果在预定的延迟时间内没有再次触发事件,则执行目标函数;否则,重新启动计时器。
举个例子,假设我们为输入框绑定了keyup事件,当用户每输入一个字符时,我们会调用某个函数进行搜索操作。如果不进行防抖处理,用户每输入一个字符都会触发一次搜索请求,这样的行为对于性能会产生很大的压力。防抖通过延迟执行搜索请求,确保只有在用户停止输入一段时间后才真正发起请求。
防抖的应用场景
输入框实时搜索
:当用户在输入框中输入内容时,触发请求进行搜索。使用防抖可以避免每次输入都发送请求,从而减轻服务器压力。
窗口调整大小
:浏览器窗口大小变化时触发resize事件,频繁触发可能导致不必要的重绘。使用防抖可以减少重绘的次数。
按钮点击
:防止在用户短时间内连续点击按钮,避免多次执行同一操作。
防抖的代码
javascript
function debounce(func, delay) {
let timer;
return function (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => func.apply(this, args), delay);
};
}
const searchInput = document.querySelector("#search");
searchInput.addEventListener("keyup", debounce(function () {
console.log("Searching for:", searchInput.value);
}, 300));
我们通过debounce函数来处理输入框的keyup事件,确保只有在用户停止输入300毫秒后才会执行搜索操作。
防抖的完善代码
javascript
const clearTimer = function clearTimer(timer) {
if (timer) clearTimeout(timer);
return null;
};
const debounce = function debounce(func, wait, immediate) {
// init params
if (typeof func !== "function") throw new TypeError("func is not a function!");
if (typeof wait === "boolean") {
immediate = wait;
wait = undefined;
}
wait = +wait;
if (isNaN(wait)) wait = 300;
if (typeof immediate !== "boolean") immediate = false;
// handler
let timer = null;
return function operate(...params) {
// now:记录是否是立即执行「第一次点击&immediate=true」
let now = !timer && immediate;
// 清除之前设置的定时器
timer = clearTimer(timer);
timer = setTimeout(() => {
// 结束边界触发
if (!immediate) func.call(this, ...params);
// 清除最后一个定时器
timer = clearTimer(timer);
}, wait);
// 如果是立即执行,则第一次执行operate就把要干的事情做了即可 "开始边界触发"
if (now) func.call(this, ...params);
};
};
二、什么是节流(Throttle)
节流是控制函数执行频率的另一种方式,它的目的是限定函数的执行频率,保证在一定时间内函数最多只执行一次。节流技术适用于那些频繁触发,但又不要求每次都执行的场景。
工作原理
节流会以固定的时间间隔执行目标函数,每次触发事件时,只有在上次函数执行的时间超过设定的间隔,才会执行目标函数。如果触发事件的频率高于设定的时间间隔,函数会被跳过。
举个例子,假设我们要监听浏览器的滚动事件,滚动事件会非常频繁地触发。如果每次触发都执行回调函数,页面性能可能会受到很大的影响。通过节流,我们可以控制滚动回调的执行频率,减少不必要的计算。
节流的应用场景
滚动监听
:滚动条位置变化时可能会频繁触发滚动事件,使用节流可以减少事件的触发频率。
窗口调整大小
:类似于防抖,节流可以限制窗口调整大小时执行的频率,避免频繁计算布局。
动画帧
:在动画过程中,控制动画回调的执行频率,避免在每一帧都做大量计算。
节流的代码
javascript
function throttle(func, delay) {
let lastTime = 0;
returnfunction (...args) {
const now = newDate().getTime();
if (now - lastTime >= delay) {
func.apply(this, args);
lastTime = now;
}
};
}
window.addEventListener("scroll", throttle(function () {
console.log("Scroll event triggered");
}, 1000));
在这个例子中,我们为scroll事件添加了节流处理,确保每1000毫秒内只执行一次回调函数,从而减少浏览器的性能消耗。
节流的完善代码
javascript
const clearTimer = function clearTimer(timer) {
if (timer) clearTimeout(timer);
return null;
};
const throttle = function throttle(func, wait) {
// init params
if (typeof func !== "function") throw new TypeError("func is not a function!");
wait = +wait;
if (isNaN(wait)) wait = 300;
// handler
let timer = null,
previous = 0; //记录上一次func触发的时间
return function operate(...params) {
let now = +new Date(),
remaining = wait - (now - previous);
if (remaining <= 0) {
// 两次触发的间隔时间超过设定的频率,则立即执行函数
func.call(this, ...params);
previous = +new Date();
timer = clearTimer(timer);
} else if (!timer) {
// 间隔时间不足设定的频率,而且还未设置等待的定时器,则设置定时器等待执行函数即可
timer = setTimeout(() => {
func.call(this, ...params);
previous = +new Date();
timer = clearTimer(timer);
}, remaining);
}
};
};
三、防抖与节流的区别
简单的应用对比
1.防抖:
适用于需要等待事件结束后执行的操作,比如用户输入结束后的搜索。
2.节流:
适用于需要固定时间间隔内执行的操作,比如滚动监听或定时任务。
四、如何选择防抖和节流?
在实际开发中,选择使用防抖还是节流,要根据具体的需求来决定:
如果事件触发频率非常高,而你只关心最终的执行结果(例如用户输入或按钮点击),那么选择防抖。
如果事件触发频率很高且需要定期执行某些操作(例如滚动监听、游戏中的帧渲染等),那么选择节流。
此外,也可以将两者结合使用,来应对复杂的应用场景。例如,滚动监听可以结合节流处理,而按钮点击事件可以使用防抖来避免多次提交。
五、防抖节流实际应用场景
1. 输入框实时搜索(防抖)
应用场景
:在输入框中实时搜索是现代Web应用中常见的功能,用户在输入关键词时,页面会根据输入的内容动态显示搜索结果。如果每次用户输入一个字符都向服务器发送请求,系统会面临大量的请求压力,甚至可能导致页面响应变慢。
解决方案
:使用防抖技术,可以避免在每次键盘输入时都发送请求。只有当用户停止输入一段时间后,才会触发请求。
javascript
// 防抖函数
function debounce(func, delay) {
let timer;
returnfunction (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => func.apply(this, args), delay);
};
}
// 输入框实时搜索
const searchInput = document.querySelector("#search");
searchInput.addEventListener("keyup", debounce(function () {
const query = searchInput.value;
console.log("Searching for:", query); // 这里可以发起真实的网络请求
}, 500));
效果
:在用户停止输入500毫秒后,才会发起一次搜索请求,极大减少了不必要的请求,提升了性能。
2. 页面滚动事件(节流)
应用场景
:当用户滚动页面时,scroll事件会频繁触发。如果不加控制,每次触发scroll事件都执行一个耗时的操作(例如,加载更多数据或计算滚动位置),可能导致页面卡顿,影响用户体验。
解决方案
:使用节流技术,限制滚动事件处理函数的执行频率,避免每次滚动时都触发大量计算。
javascript
// 节流函数
function throttle(func, delay) {
let lastTime = 0;
returnfunction (...args) {
const now = newDate().getTime();
if (now - lastTime >= delay) {
func.apply(this, args);
lastTime = now;
}
};
}
// 页面滚动加载更多
window.addEventListener("scroll", throttle(function () {
const scrollPosition = window.scrollY + window.innerHeight;
const documentHeight = document.documentElement.scrollHeight;
// 判断是否接近页面底部,进行数据加载
if (scrollPosition >= documentHeight - 100) {
console.log("Loading more content...");
// 发起加载更多数据的请求
}
}, 200)); // 每200ms执行一次
效果:即使用户快速滚动页面,也会限制scroll事件处理函数的调用频率,每200ms才执行一次,减少不必要的计算和DOM操作。
效果
:即使用户快速滚动页面,也会限制scroll事件处理函数的调用频率,每200ms才执行一次,减少不必要的计算和DOM操作。
3. 窗口调整大小(节流)
应用场景
:当用户调整浏览器窗口大小时,resize事件会频繁触发。如果在每次触发时进行重计算(如重新布局、调整元素大小等),可能导致页面性能下降,特别是在响应式设计的页面中。
解决方案
:使用节流技术,减少resize事件处理的频率,避免重复执行布局计算。
javascript
// 节流函数
function throttle(func, delay) {
let lastTime = 0;
returnfunction (...args) {
const now = newDate().getTime();
if (now - lastTime >= delay) {
func.apply(this, args);
lastTime = now;
}
};
}
// 监听窗口调整大小
window.addEventListener("resize", throttle(function () {
console.log("Window resized:", window.innerWidth, window.innerHeight);
// 执行需要重新计算的逻辑,如重新调整布局
}, 300)); // 每300ms执行一次
效果
:通过节流技术,确保在窗口调整大小时不进行过多的计算,从而提高页面的流畅度。
4. 表单提交按钮防止重复点击(防抖)
应用场景
:在一些表单提交场景中,用户可能在短时间内多次点击提交按钮,导致多次请求发出,甚至出现重复提交。常见的场景如支付、注册等。
解决方案
:使用防抖技术,防止用户短时间内多次点击按钮,确保表单只提交一次。
javascript
// 防抖函数
function debounce(func, delay) {
let timer;
returnfunction (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => func.apply(this, args), delay);
};
}
// 防止重复提交表单
const submitButton = document.querySelector("#submitBtn");
submitButton.addEventListener("click", debounce(function () {
console.log("Form submitted!");
// 发送表单请求
// submitForm();
}, 1000)); // 1000ms内只触发一次提交
效果
:即使用户快速多次点击按钮,也只会触发一次提交操作,防止重复提交。
5. 图片懒加载(节流)
应用场景
:在长列表或长页面中,图片通常会根据用户滚动加载。scroll事件的触发频率可能非常高,如果每次滚动都计算图片是否需要加载,可能会造成性能问题。
解决方案
:使用节流控制滚动事件的处理频率,确保只在合适的时间点计算哪些图片需要加载。
javascript
// 节流函数
function throttle(func, delay) {
let lastTime = 0;
returnfunction (...args) {
const now = newDate().getTime();
if (now - lastTime >= delay) {
func.apply(this, args);
lastTime = now;
}
};
}
// 图片懒加载
const images = document.querySelectorAll("img.lazyload");
function lazyLoadImages() {
images.forEach(img => {
if (img.getBoundingClientRect().top < window.innerHeight && !img.src) {
img.src = img.dataset.src; // 从data-src属性加载图片
img.classList.remove("lazyload");
}
});
}
window.addEventListener("scroll", throttle(lazyLoadImages, 300)); // 每300ms触发一次
效果
:通过节流,减少每次滚动时对图片的加载检查,确保只有在需要加载图片时才触发计算,优化页面性能。
6. 表单验证(防抖)
应用场景
:用户在表单中输入数据时,需要进行实时的字段验证。如果每次用户输入时都触发验证逻辑,会导致页面性能下降,尤其是在多个输入字段的情况下。
解决方案
:使用防抖,确保只有在用户停止输入一段时间后才进行表单验证,从而减少不必要的验证请求。
javascript
// 防抖函数
function debounce(func, delay) {
let timer;
returnfunction (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => func.apply(this, args), delay);
};
}
// 实时验证用户名
const usernameInput = document.querySelector("#username");
usernameInput.addEventListener("input", debounce(function () {
const username = usernameInput.value;
console.log("Validating username:", username);
// 发起验证请求
}, 500)); // 输入停止500ms后触发验证
效果
:防止在每次用户输入时都进行验证,只在用户停止输入500毫秒后触发一次验证,提升页面响应速度。
六、总结
防抖
:适用于需要等待事件完成后执行一次的场景。
节流
:适用于需要限制执行频率的场景。