前言
在前端开发中,性能优化是一个永恒的话题。而防抖(Debounce)和节流(Throttle)是两个常用的性能优化手段,它们可以有效控制事件触发的频率,提升用户体验,减轻系统负担。在本文中,我们将深入理解防抖和节流的概念,并通过手写代码来实现它们。
先看个效果(了解完防抖和节流再回来看一遍,会有不一样的感觉):
防抖(Debounce)
防抖的核心思想是在一段连续操作结束后,只执行一次相应的操作。比如用户频繁输入时,我们希望在用户停止输入一段时间后才触发相应的处理函数。
为什么需要防抖?
xml
<body>
<form action="https://www.fastmock.site/mock/39ac87de3060aa2bb2ba20a0ff375c81/cat-movie/mostLike" >
<div>
<label for="user">账号:</label>
<input type="text" id="user">
</div>
<div>
<label for="password">密码:</label>
<input type="password" id="password">
</div>
<button id="btn" onclick="submit">提交</button>
</form>
</body>
<script>
let btn = document.getElementById('btn');
btn.addEventListener('click',() => {
console.log('已提交')
},1000);
</script>
上述代码会生成如下:
我们都知道,在以后的开发中,用户点击提交按钮,会将表单的中的数据提交到后台服务器中去,数据只需要提交一次即可,可当用户进行多次频繁点击提交按钮时,会发生多次提交(即使后面的数据可能什么也没有),我们可以通过上述模拟案例多点击几次测试一下:
而我们如果允许用户多次点击就多次提交数据,显然会加重系统的负担,这也就是防抖诞生的原因,防抖的出现正是为了解决这一类问题(主要是处理用户输入事件,特别是在搜索框或实时搜索的场景中),上述代码只是个简单的案例。
如何解决上述问题?
- 防抖的诞生
为了方便展示,我们只单独写一个提交按钮,其中如下:
案例1:提交按钮的防抖
Html
<body>
<button id="btn">提交</button>
<script>
let btn = document.getElementById('btn')
function send(e) {
console.log(this, '提交完成', e);
}
btn.addEventListener('click', debounce(send, 1000))
function debounce(fn, delay) {
let timer;
return function() {
let args = arguments
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.call(this, ...args)
}, delay)
}
}
</script>
</body>
我们看一下实现效果:
- 如下述gif图所示,我先狂点提交按钮,控制台并没有疯狂输出,当我们停下来1s后才会输出。接着,我没有狂点按钮,而是正常点击,控制台也会在我点击1s后打输出。然后又是狂点按钮,效果与一开始点的一样,
防抖的原理
原理解析
防抖的实现原理主要是通过设置一个定时器,在事件触发后延迟一段时间再执行函数。如果在这段时间内再次触发了相同的事件,则清除之前的定时器,重新设置一个新的定时器,保持延迟执行的效果。
javascript
// 防抖
function debounce(fn, delay) {
let timer;
return function() {
// arguments为一个特殊的对象,他包含了函数调用时传递的所有参数
let args = arguments
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
//使用call()方法的目的是确保在setTimeout的回调函数中调用原始的fn函数时,
//函数内部的this指向仍然保持在正确的上下文,使原始函数fn内部的this指向不会被改变
fn.call(this, ...args)
//...args则是将debounce函数内部匿名函数被调用时传入的参数传递给fn函数
}, delay)
}
}
案例2:输入框搜索的防抖
javascript
function handleSearchDebounced(query) {
console.log('Searching for:', query);
}
const debouncedSearch = debounce(handleSearchDebounced, 300);
const searchInput = document.getElementById('search-input');
searchInput.addEventListener('input', function(event) {
debouncedSearch(event.target.value);
});
相信看了上述案例1的解释,案例2大家自己看起来也是游刃有余了。它在日常生活中大家肯定见过它,比如我们搜索某个音乐,我们输入一个d,下面会出现有关d的提示词,但我们连续输入dong,(相信大家的手速肯定快,一个字母之间应该不要300ms吧),不会直接出现有关d的提示词,当然,如果你每个字母敲的时间大于300ms,它仍然会出现有关d的提示词,甚至你输入'o'后停留,提示词肯定是有关'do'的了。
- 在上述案例2中,我们使用了
debounce
函数对输入框的输入事件进行了防抖处理,确保在300毫秒内只执行一次handleSearchDebounced
函数,避免频繁触发搜索操作。
实际应用如下:
下面介绍本文另一个主要内容--节流
节流(Throttle)
节流的核心思想是在一定时间内,控制函数的执行频率,确保在规定的时间间隔内只执行一次。这样可以有效减少函数的执行次数,提高页面性能。
先看案例代码和实现效果:
xml
<body>
<button id="btn">提交</button>
<script>
let btn = document.getElementById('btn')
function send(e) {
console.log('提交了', e);
}
btn.addEventListener('click', throttle(send, 1000))
function throttle(fn, delay) {
let prevTime = Date.now()
return function() {
if (Date.now() - prevTime > delay) {
fn.apply(this, arguments)
prevTime = Date.now()
}
}
}
</script>
</body>
上述GIF图我们可以看到,即使我们快速点击,控制台也是有规律的输出。这就是节流的体现。
节流的原理与实现
原理解析
节流的实现原理相对简单,主要是通过记录上一次函数执行的时间,然后在新的事件触发时判断距离上一次执行是否超过了设定的时间间隔,如果超过则执行函数,否则忽略。
基本实现
javascript
function throttle(fn, delay) {
// 记录上一次函数的执行时间戳
let prevTime = Date.now()
return function() {
// 计算当前时间戳与上一次执行时间戳的差值,判断是否满足节流的条件
if (Date.now() - prevTime > delay) {
fn.apply(this, arguments)
// 更新上一次执行时间戳
prevTime = Date.now()
}
}
}
那节流在日常使用中有具体体现吗?
- 有的,节流处理滚动事件
问题:滚动事件通常涉及到大量的事件触发,因为用户在滚动页面时会不断触发滚动事件。每次滚动事件触发都可能导致相关的事件处理函数执行,例如更新页面元素、计算滚动位置等。如果不对滚动事件进行节流容易导致性能问题,增加浏览器的负担。如果事件处理函数执行的逻辑复杂或涉及大量DOM操作,可能会导致页面性能下降,造成卡顿或滑动不流畅的感觉。同时也会造成资源的浪费,因为有些处理可能是多余的。
案例:滚动事件的节流
代码:
javascript
function handleScrollThrottled() {
console.log('Scrolled!');
}
const throttledScroll = throttle(handleScrollThrottled, 200);
window.addEventListener('scroll', throttledScroll);
- 这个案例展示了如何使用throttle函数来对滚动事件进行节流处理,确保在200毫秒内只执行一次handleScrollThrottled函数。
节流与防抖的选择
在实际开发中,选择使用节流还是防抖取决于具体的业务场景和需求。
- 如果需要频繁执行函数,但又希望控制执行的频率,例如处理用户输入时的搜索建议,可以选择使用节流。
- 如果在一定时间内只希望执行一次函数,例如处理窗口调整大小事件时的布局调整,可以选择使用防抖。
应用场景总结
节流的应用场景
- 滚动加载数据
- 在滚动事件中,如果需要在用户滚动页面时加载更多数据,使用节流可以确保在一定时间内只发起一次数据加载请求,避免过于频繁的请求。
ini
const loadDataThrottled = throttle(loadData, 500);
window.addEventListener('scroll', loadDataThrottled);
- 窗口调整大小
- 当用户调整窗口大小时,可能需要进行一些布局的调整。使用节流可以确保在一定时间内只执行一次布局调整,提高性能
ini
const adjustLayoutThrottled = throttle(adjustLayout, 300);
window.addEventListener('resize', adjustLayoutThrottled);
防抖的应用场景
- 输入框搜索
- 在输入框输入时,如果需要进行实时搜索并展示搜索结果,使用防抖可以确保在用户停止输入一段时间后再触发搜索,减少请求次数。
ini
const searchDebounced = debounce(search, 300);
searchInput.addEventListener('input', function(event) {
searchDebounced(event.target.value);
});
- 提交表单验证
- 在表单提交时进行数据验证,使用防抖可以确保用户停止输入后再执行验证,避免频繁验证。
ini
const validateFormDebounced = debounce(validateForm, 500);
submitButton.addEventListener('click', validateFormDebounced);
总结
JavaScript节流和防抖是在处理用户交互时常用的性能优化手段。通过合理地使用这两种技术,我们可以有效地控制函数的执行频率,提升用户体验,并减轻浏览器的负担。在实际应用中,根据具体的业务场景和需求选择合适的方案,可以更好地优化代码性能。
留言
感谢您的查阅,如果觉得对您有帮助的话,可以点赞评论加收藏哦,一键三连♥(ˆ◡ˆԅ),关注博主不迷路。感谢您的支持。