为什么要使用 节流 & 防抖 呢
最主要的作用当然是用于提升性能。虽然大部分情况不使用他们代码也能跑,程序一样动,但是如搜索提示功能,在没有运用节流的情况下,用户一直打字,你的代码就会一直请求ajax,network上刷出一片的接口请求,会对服务器端造成极大的压力。用节流优化后,能大幅度的减少服务器的负载,减轻压力
节流(throttle)
节流是什么呢
概念性的讲就是:一个事件只能在间隔一段时间内最多生效一次,不管你在间隔时间内触发了多少次,也就只有第一次生效
大佬的理解:函数节流就是fps游戏的射速,就算一直按着鼠标射击,也只会在规定射速内射出子弹
使用场景
监听滚动条滚动事件
具体实现
有两种比较常见的解决办法:
定时器写法
// 节流
const throttle = function() {
let args = [...arguments],
timeout = args[0],
fns = args.slice(1), // 回调函数数组,当你是自定义事件时可以传入多个方法进行触发
canRun = true,
timer;
return function() {
if (canRun) {
timer = setTimeout(() => {
canRun = true
}, timeout);
for (const fn of fns) {
fn.call(this, ...arguments)
}
canRun = false
}
}
}
window.onscroll = throttle(1000, function () {
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
console.log(`滚动距离:${scrollTop}`);
})
时间戳写法
// 节流
const throttle = function() {
let args = [...arguments],
timeout = args[0],
fns = args.slice(1), // 回调函数数组,当你是自定义事件时可以传入多个方法进行触发
before;
return function() {
let now = new Date()
before = before || new Date()
if (before && now - before >= timeout) {
for (const fn of fns) {
fn.call(this, ...arguments)
}
before = now
}
}
}
window.onscroll = throttle(1000, function () {
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
console.log(`滚动距离:${scrollTop} ${new Date()}`);
})
防抖(debounce)
防抖又是什么呢
概念性的讲就是:一个事件只能在上一次触发一段时间后,才能继续触发,如果提前触发就重新计时
大佬的理解:函数防抖就是法师发技能的时候要读条,技能读条没完再按技能就会重新读条
使用场景
具体实现
<body>
<input type="text">
<button>点击</button>
</body>
<script>
// 防抖
const debounce = function () {
let args = [...arguments],
timeout = args[0],
fns = args.slice(1), // 回调函数数组,当你是自定义事件时可以传入多个方法进行触发
timer;
return function () {
clearTimeout(timer)
timer = setTimeout(() => {
for (const fn of fns) {
fn.call(this, ...arguments)
}
}, timeout);
}
}
var inputGetAjax = debounce(1000, function (val) {
console.log(`输入框此时文字:${this.value}`);
})
var btnGetAjax = debounce(1000, function (val) {
this.innerText = '我被执行了'
console.log('按钮被执行了');
})
const input = document.querySelector('input')
const btn = document.querySelector('button')
input.addEventListener('input', inputGetAjax)
btn.addEventListener('click', btnGetAjax)
</script>
但是这种写法第一次触发的时候是不会立即触发的,而是会等待计时器计时完毕才会执行回调函数。
如果我想第一次触发马上就执行一次该怎么做呢
只需要加上一个判断变量就可以了
// 省略相同代码...
// 防抖
const debounce = function () {
let args = [...arguments],
timeout = args[0],
fns = args.slice(1), // 回调函数数组,当你是自定义事件时可以传入多个方法进行触发
canRun = true, // <--- 新增的地方
timer;
return function () {
if (canRun) { // <--- 新增的地方
for (const fn of fns) {
fn.call(this, ...arguments)
}
canRun = false
} else { // <--- 新增的地方 👇
clearTimeout(timer)
timer = setTimeout(() => {
canRun = true
}, timeout);
} // <--- 新增的地方 👆
}
}
// 省略相同代码...
归纳
想固定频率执行的用节流,不想进行持续操作的时候用防抖。
相关链接
完全看不懂我写什么的可以去拜读大佬的文章