众所周知,防抖(Debounce)和节流(Throttle)是前端面试题中频繁出现的考题,因为这哥俩是前端开发中常用的性能优化手段,特别是在处理高频触发的事件如scroll
、resize
、keyup
等时,这些操作若未经优化,可能会导致大量的函数调用,从而增加CPU负荷、影响页面响应速度,甚至造成用户界面卡顿,严重影响用户体验。而防抖(Debounce)和节流(Throttle)能够有效减少不必要的计算和网络请求,从而减轻服务器压力,提升用户体验。下面我将深入浅出地讲解这两个概念,一起解决这个常考面试题。
搭建简单的后端
不过在此之前,我们可以先去搭建一个简单的后端来用于返回静态数据来模拟一下实现前后端交互的场景。
-
首先,可以创建一个文件夹,其中包含俩个分别可以存放前后端代码的文件
-
打开backend后端的集成终端,输入
npm init -y
命令进行初始化 -
输入
npm i json-server
命令来 安装json-server
,这是一个轻量级的Node.js包,用于快速搭建一个模拟的REST API服务器 -
创建一个JSON文件作为数据源,例如
db.json
准备数据文件。在这个文件中,定义想要模拟的资源和数据结构。 -
将
package.json
中准备一个脚本,将scripts
中的代码改为"dev": "json-server --watch db.json"
-
输入
npm run dev
命令,获得俩个地址来访问你的项目
之后,我们就可以开始编写前端的代码了,来实现我们的防抖和节流。
防抖(Debounce)
-
概念:防抖技术确保某函数在一定时间内只执行一次。如果在这段时间内函数被多次调用,只有最后一次调用会在延迟时间结束后执行。这非常适合搜索框的自动完成场景,避免用户每输入一个字符就发送一次请求。
-
应用场景:搜索框的自动建议、表单验证、窗口大小调整事件处理等。
-
实现原理 :利用
setTimeout
设置延时执行,并在每次触发时清除前一次的延时,确保只有最后一次操作会触发实际的处理函数。
不防抖的(unDebounce)
我们不妨先来看看不防抖的是怎么样的:
- 代码部分
html
<div>
没有防抖的input
<input
type="text"
id="unDebounce"
placeholder="请输入你要搜索的用户名"
>
</div>
js
function handleNameSearch(e){
const value=e.target.value;
fetch('http://localhost:3000/users')
.then(res=>res.json())
.then(data=>{
const users=data;
const filterUsers = users.filter(users=>{
return users.name.includes(value)
})
console.log(filterUsers);
})
}
inputa.addEventListener("keyup",handleNameSearch)
- 运行结果:
上面结果所示,只要我们在输入框中每次输入文字,那么就会触发一次模拟请求,这对于用户和开发者而言都是不好的体验和资源的浪费。要是有很多个用户端同时请求服务器,那服务器的压力巨大,直接会被干冒烟,所以要注意一定的频度,减少请求次数,减轻一下服务器的压力,所以防抖就被推出来了。
防抖的(debounce)
防抖思路:利用闭包的功能,在键盘输入时触发事件处理函数中的定时器
setTimeout
,而当我再次在键盘输入时清除之前的定时器clearTimeout
,再重新设置一个定时器setTimeout
,直到我们不从键盘输入,过了指定时间后才输出我们需要的结果
来看看防抖后的:
- 代码部分
html
<div>
防抖后的input
<input type="text" id="debounce" placeholder="请输入你要搜索的用户名">
</div>
js
const inputb=document.getElementById("debounce");
function handleNameSearch(e){
const value=e.target.value;
fetch('http://localhost:3000/users')
.then(res=>res.json())
.then(data=>{
const users=data;
const filterUsers = users.filter(users=>{
return users.name.includes(value)
})
console.log(filterUsers);
})
}
// 闭包功能函数
function debounce(func,delay){
//返回值必须是函数,因为要服务于keyup,事件处理函数
return function(args){
clearTimeout(func.id)
// 函数是对象,id挂载在func上,func是闭包中的自由变量
func.id=setTimeout(()=>{
func(args);
},delay)
}
}
const debounceNameSearch =debounce(handleNameSearch, 1000)
inputb.addEventListener("keyup",debounceNameSearch)
- 运行结果:
从上面的运行结果可以看出,在1000ms
内在输入框中连续输入文字都不会触发请求事件,而是在输入框停止输入后的1000ms
后发送请求。
实现原理很简单,每当用户频繁在输入框中键入字符(触发keyup
事件)时,我们并非立即执行查询操作,而是启动一个新的定时器。如果在定时器到期之前又有新的输入发生,我们会取消当前的定时器并重新启动一个新的定时器。这样一来,只有当用户停止输入一段时间(1000ms
)或者俩次输入的时间间隔超过delay的时间后,最后一次输入触发的定时器才会执行,进而调用实际的处理函数。
节流(Throttle)
-
概念:节流确保函数在指定的时间间隔内至多执行一次。不论在这段时间内事件触发多少次,函数只会按照既定的时间间隔执行。
-
应用场景:滚动事件监听、窗口的resize事件处理等,适合需要持续响应但不必即时反馈的场景。
-
实现原理 :同样使用
setTimeout
,但在函数执行后才开始计时,确保每隔一段时间至少执行一次。 -
代码部分
html
<div>
节流后的input
<input type="text" id="throttle" placeholder="请输入你要搜索的用户名">
</div>
js
const inputc=document.getElementById("throttle");
function handleNameSearch(e){
const value=e.target.value;
fetch('http://localhost:3000/users')
.then(res=>res.json())
.then(data=>{
const users=data;
const filterUsers = users.filter(users=>{
return users.name.includes(value)
})
console.log(filterUsers);
})
}
function throttle(func,delay){
let inThrottle=false;
return function(args){
if(!inThrottle){
func(args);
inThrottle=true;
setTimeout(()=>{
inThrottle=false;
},delay)
}
}
}
const throttleNameSearch =throttle(handleNameSearch, 3000)
inputc.addEventListener("keyup",throttleNameSearch)
- 运行结果:
从上面运行结果来看,当你首次在输入框中按下键盘时,会立即检查是否处于节流状态(inThrottle
标志)。如果当前不是节流期间(即inThrottle
为false
),则会立即执行事件请求函数,同时设置inThrottle
为true
以阻止后续的立即调用。随后,通过setTimeout
设置了一个3000ms
的延迟,在此期间,即使用户继续在输入框中输入,也不会触发新的搜索请求,因为if(!inThrottle)
会阻止这些输入触发的函数执行。
一旦3000ms
的延迟结束,inThrottle
会被重置为false
,此时如果用户仍在继续输入,下一次键盘输入事件将能够再次触发事件请求函数,整个过程重新开始。这意味着,从首次触发到下一次有效触发之间,至少会间隔3000ms
,确保了在设定的时间窗口内,即使有连续的输入事件,请求也只会被发送一次,有效地防止了因频繁输入而导致的大量不必要的API调用,达到了节流的效果。
总结:
防抖(Debounce)的核心要点:
- 目的:确保函数在最后一次调用后的固定延迟后执行,如果在这段时间内又被调用,则重新计时。适用于如表单验证、搜索建议等场景,减少不必要的计算或网络请求。
- 实现机制 :利用
setTimeout
和clearTimeout
,在每次函数被调用时清除前一个延时器并重新设置,确保只有在最后一次调用后的一段时间没有新的调用,函数才会执行。
节流(Throttle)的核心要点:
- 目的:确保函数在给定的时间间隔内最多执行一次,即使在这段时间内事件被触发多次。适用于滚动事件处理、窗口大小调整等需要限制执行频率但保持连续响应的场景。
- 实现技巧 :同样依赖于
setTimeout
,但与防抖不同的是,它在函数执行后开始计时,确保每隔一段时间至少执行一次,通过一个标志(如inThrottle
)来判断是否允许执行函数。
通过对比防抖和节流的实现及应用场景,我们发现两者都是为了提高应用程序的性能和用户体验,但侧重点不同。防抖关注于避免短时间内连续的重复操作,确保最后的操作被执行;而节流则侧重于控制函数执行的频率,保证在高频率触发的场景下仍能维持一定的响应性。
如果文章对你有帮助的话,不妨点点关注✨点点赞👍(一键三连是真爱💞),这将是我持续学习和更新的动力😁😁😁