| 1.防抖
防抖,顾名思义就是防止用户“手抖”持续输出,把多次向服务器请求相同数据,把服务器干冒烟了。这时候我们需要设置“防抖”,“一个大招用后几秒后点击才能继续使用”。
应用场景:
输入框搜索建议:当用户输入时,防抖可以用于延迟发送请求,避免频繁请求后端接口。窗口大小调整:当窗口大小调整时,防抖可以用于调整事件触发的频率,避免频繁操作导致页面抖动。按钮防重复点击:当用户频繁点击按钮时,防抖可以用于限制按钮点击的频率,避免重复提交操作。
下面我用第一个(输入框搜索)应用场景,带着大家一步步手写“防抖”效果代码。
其中数据采用json-server 进行数据管理,创建后端user数据。如果不知道怎么创建环境,可以看看下面“浪遏”大佬这篇文章
{"users": [
{
"id": "1",
"name": "史铁兵"
},
{
"id": "2",
"name": "刘红旗"
},
{
"id": "3",
"name": "张三"
}
]
}
下面没有使用防抖情况,实现用户输入,搜索请求后端数据。
首先,我们在body创建搜索框,并且当地到监听事件,执行使用fetch从后端拉起数据,再使用filter过滤出对应includes数据,这里使用es6新增方法map 映射把用户姓名打印到页面。其中使用join转换成字符串。
div>
label for="unDebounceInput">用户搜索label>
input type="text"
id="unDebounceInput"
placeholder="输入需要搜索的用名字">
div>
ul id="user">ul>
script>
const oUL = document.querySelector('#user');
// reset api 接口 ,通往后端接口 fetch 函数
const oInput = document.getElementById('unDebounceInput');
// keyup 事件, 当用户输入时触发;事件处理函数,this指向oInput事件源
oInput.addEventListener('keyup',function() {
let val = this.value.trim();
if(val == '') {
oUL.innerHTML = '';return;// 清空ul
}
fetch('http://localhost:3001/users')
.then(res => res.json())
.then(user => {
// 过滤用户,
const result = user.filter(item => item.name.includes(this.value))
// Array 在es6中新增的方法
oUL.innerHTML = result.map(item => `${item.name} `).join('')
})
})
script>
body>
事件绑定keyup,不断向后端请求数据
下面代码使用防抖,减少因为用户多次向服务器访问出现卡顿问题。封装函数debounceNameSearch,并且传入fn和定时时间1s。看下面代码:
实现一下fn函数
// fn 函数
function fn() {
let val = this.value.trim(); // 去除空格
if(val == '') {
oUL.innerHTML = '';return;// 清空ul
}
fetch('http://localhost:3001/users')
.then(res => res.json())
.then(user => {
// 过滤用户,
const result = user.filter(item => item.name.includes(this.value))
// Array 在es6中新增的方法
oUL.innerHTML = result.map(item => `${item.name} `).join('')
})
}
主要看一下debounce代码:这里使用apply处理fn,处理this指向问题,不是指向window,指向oInput;使用类数组arguments。
// defounce函数
function debounce(fn,delay) {
let timer = null;
return function() {
// 指定的时间后执行,使用类数组传递参数
timer = setTimeout(() => {
console.log(this);
},delay)
}
}
在这段代码里面,使用debounce函数,通过传入fn ,delay作为参数,实现delay时间后,执行fn函数。我详细看看dedounce函数任何实现防抖。当用户输入值进行查询时候,调用debounce(fn,1000),增加1s后延迟实现fn调用,只有当用户不在输入后才执行需要的事件,这里就是fn()方法。
这样直到用户输入完成1s后执行输出打印效果。这时候在debounce函数声明一个timer变量,用来记录定时器的名字,然后在返回函数中,我们将每次点击创建的定时器赋值给timer,所以我们只要在这之前判断timer是否有值,如果timer有值,说明上一次点击事件创建的定时器还没有执行完,所以这时我们只需把上次的定时器清空,则不会再执行上次的点击事件了,直到在延时时间内不再点击,则才会执行我们需要执行的事件。这时代码如下:
// defounce函数
function debounce(fn,delay) {
let timer = null;
return function() {
if(timer) {
clearTimeout(timer);// 清除定时器
}
// 指定的时间后执行,使用类数组传递参数
timer = setTimeout(() => {
fn.apply(this,arguments)//
},delay)
}
}
到这里我们的防抖代码就成功实现啦!这里面每一条代码都运用的非常巧妙,需要我们对闭包,this指向,以及第二种方法箭头函数的知识都要了解的非常清楚,知道了这些,是不是觉得手写防抖代码很简单啦~
| 2.节流(Throttle)
节流,通俗将就是一个事件持续发生,需要限制一个时间间隔执行。比如,王者荣耀开完大招,总是点击还是没有用,只有到一定时间后才可以发送。。下面是实现步骤流程图:
应用场景:
页面滚动加载:当用户滚动页面时,节流可以用于限制加载事件的触发频率,避免过多的加载请求。频繁点击按钮:当用户频繁点击按钮时,节流可以用于限制按钮点击的频率,避免过于频繁的操作。
button id="btn" class="btn">点击加载button>
script>
function fn() {
console.log('Hello', this);
}
// 确保在 DOM 完全加载后获取元素
document.addEventListener('DOMContentLoaded', (event) => {
const btn = document.getElementById("btn");
btn.addEventListener("click", throttle(fn, 1000));
});
function throttle(fn, limit) {
let pretime = 0; // 初始化为 0
return function() {
const now = Date.now();
if (now - pretime > limit) {
fn()
pretime = now;
}
};
}
script>
通过使用Date.now()和now和限制时间进行计算,实现节流操作。
同样的使用apply让this从window指向btn
function throttle(fn, limit) {
let pretime = 0; // 初始化为 0
return function() {
const now = Date.now();
if (now - pretime > limit) {
fn.apply(this, arguments); // 使用 apply 方法确保 this 正确指向
pretime = now;
}
};
}
这样虽然多次点击加载,但是还是在limit时间后执行一次事件
总结
防抖:在规定时间内,多次触发只响应最后一次。(多次触发,只执行最后一次)
节流:在规定时间内,多次触发只响应第一次。(规定时间内,只触发一次)
以上代码分别展示了防抖和节流的实现方式。我们可以根据实际需要选择防抖还是节流来优化事件触发的频率,提升用户体验。