目录
- 1,区别
- 2,使用
- 3,实现
-
- 3.1,debounce
- 3.2,throttle
- 4,意外收获
throttle节流,debounce防抖
1,区别
假设时间频率:1s
throttle 是每隔 1s,必然执行。
高铁不能人,到点就发车。
debounce 是触发后必须等待 1s 后才执行,如果等待过程中再次被触发,则重新计时。
电梯来人就感应不关门,直到没人才关闭。
基于此,常见场景:
-
click
,为了防止连续点击,应设置在点击停止一段时间后,再执行 click 事件。(debounce)
resize 同理,得文档视图调整停下后,再执行 resize 事件。(debounce) -
scroll
,会执行的很频繁,一般会将 scroll 事件隔一段时间执行。(throttle)
滚动停止后再执行的逻辑,工作场景中可能会遇到。(debounce) -
mousemove
和scroll
同理(throttle) -
change
,一般不用做限制,如果是输入框,得失焦后才触发。其他的下拉框单选框什么的,也不会很频繁的调用。 -
input
,简单表单输入,到也不用做限制。
如果是输入后要进行搜索,那肯定要加限制,等输入停止一段服务器托管网时间后,再执行搜索。(debounce)
即时搜索不做限制,比如浏览器的搜索行为,或者只是过滤本地数据。
2,使用
以 throttle-debouncenpm
依赖为例。
import { throttle } from 'throttle-debounce'
function onScroll(event) {
console.log(event)
// ...
}
const _scroll = throttle(200, onScroll)
window.addEventListener('scroll', _scroll)
template>
div>
button @click="debounceClick(false, $event)">debouncebutton>
div>
template>
script>
import { debounce } from 'throttle-debounce'
export default {
methods: {
debounceClick: debounce(300, function(bool, event) {
console.log(bool, event)
})
}
}
script>
3,实现
参考1,参考2,也参考了 throttle-debounce^1.0.1 | npm 源码。
3.1,debounce
基础实现
function debounce(delay, callback) {
let timeoutId = 0
function wrapper() {
let self = this
let args = arguments
function exec() {
callback.apply(self, args)
}
// 只要调用就清理,否则就耐心等待 setTimeout 执行。
clearTimeout(timeoutId)
timeoutId = setTimeout(exec, delay)
}
return wrapper
}
关于
this
的问题,因为this
应指向返回的wrapper
函数。
问题:即便只点一次,也得等 delay
之后才能触发。
可以加个判断,是在开始触发 或 等待 delay
后触发。
function debounce(delay, callback, atBegin) {
let timeoutId = 0
function wrapper() {
let self = this
let args = arguments
function exec() {
callback.apply(self, args服务器托管网)
}
clearTimeout(timeoutId)
if (atBegin) {
let callNow = !timeoutId
timeoutId = setTimeout(function() {
timeoutId = 0
}, delay)
if (callNow) {
exec()
}
} else {
timeoutId = setTimeout(exec, delay)
}
}
return wrapper
}
或是如下写法
function debounce(delay, callback, atBegin) {
let timeoutId = 0
function wrapper() {
let self = this
let args = arguments
function exec() {
callback.apply(self, args)
}
function clear () {
timeoutId = 0;
}
clearTimeout(timeoutId)
if (atBegin && !timeoutId) {
exec()
}
timeoutId = setTimeout(atBegin ? clear : exec, delay)
}
return wrapper
}
3.2,throttle
function throttle(delay, callback) {
let timeoutId = 0
let lastExec = 0
// 替代后的函数
function wrapper() {
let args = arguments
let self = this
let elapsed = Number(new Date()) - lastExec // 上次执行后经过的时间
function exec() {
lastExec = Number(new Date()) // 记录执行的时间点
callback.apply(self, args)
}
clearTimeout(timeoutId)
if (elapsed > delay) {
exec()
} else {
timeoutId = setTimeout(exec, delay - elapsed)
}
}
return wrapper
}
上面的实现,有个问题就是 trailing
(尾随)效果,也就是 else 是否需要触发。
比如对滚动页面来说,滚动已经停止了,但可能停止的时间点正好是 elapsed ,那再等
delay - elapsed
时间后,还会再触发一次。
只需要加个判断即可。
需要注意的是,npm 依赖的参数 noTrailing
意思正好相反,noTrailing = false
时,添加尾随效果。
function throttle(delay, callback, trailing = false) {
let timeoutId = 0
let lastExec = 0
// 替代后的函数
function wrapper() {
let args = arguments
let self = this
let elapsed = Number(new Date()) - lastExec // 上次执行后经过的时间
function exec() {
lastExec = Number(new Date()) // 记录执行的时间点
callback.apply(self, args)
}
clearTimeout(timeoutId)
if (elapsed > delay) {
exec()
} else if (trailing) {
timeoutId = setTimeout(exec, delay - elapsed)
}
}
return wrapper
}
4,意外收获
函数参数顺序问题。在 throttle-debounce^1.0.1 | npm 源码。中,有如下代码:
module.exports = function ( delay, noTrailing, callback, debounceMode ) {
// ...
// `noTrailing` defaults to falsy.
if ( typeof noTrailing !== 'boolean' ) {
debounceMode = callback;
callback = noTrailing;
noTrailing = undefined;
}
// ...
}
原来当某个参数值和想要的不相符时,可以直接替换位置!
以上。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net