搞定节流与防抖

268 阅读2分钟

节流

  • 规定在一个单位时间内,只能触发一次函数,如果这个单位时间内触发多次函数,只有一次生效
  • 好比玩游戏时法师释放的技能,它有冷却时间,在一定时间内,只能释放一次技能

原理

原理是通过判断是否到达规定时间来触发函数。

运用场景

  • 鼠标不断点击触发,mousedown 事件的执行(单位时间内只触发一次)
  • 监听滚动事件,比如是否滑到底部自动加载更多,用节流来判断

代码实现

用一个可拖拽的div模拟:

结果:

导致移动一点点都会触发事件,打印了好多😭,设想如果里面设计dom查询之类的,那么性能定会堪忧,so,节流来帮忙!

const div1 = document.getElementById('div1')
let timer = null
div1.addEventListener('drag', function (e) {
    if (timer) {
        return
    }
    timer = setTimeout(() => {
        console.log(e.offsetX, e.offsetY)

        timer = null
    }, 100)
})

结果为每隔100毫秒打印一次😄,为了方便封装一哈:

const div1 = document.getElementById('div1')
function throttle(fn, delay = 100) {
    let timer = null

    return function () {
        if (timer) {
            return
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments)
            timer = null
        }, delay)
    }
}

div1.addEventListener('drag', throttle(function (e) {
    console.log(e.offsetX, e.offsetY)
}))

防抖

  • 当持续触发事件时,一定时间段内没有再次触发事件,事件处理函数才会执行一次,如果在设定的时间到来之前,又一次触发了事件,就重新开始延时
  • 好比玩游戏时英雄回泉水,当点击了回泉水时,就不能中断,如果中断就得重新来过

原理

其原理是维护一个计时器,规定在 delay 时间后触发函数,但是在 delay 时间内再次触发的话,就会取消之前的计时器而重新设置;这样一来,只有最后一次操作能被触发。

运用场景

  • input 输入框实现模糊匹配功能,用户在不断输入值时,用防抖来节约请求资源
  • window 触发 resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次

代码实现

input 输入框未做处理时

结果:

显而易见,每次输入都会触发,这很不理想,那么用防抖优化一哈,让它等个500毫秒:

const input1 = document.getElementById('input1')
let timer = null
input1.addEventListener('keyup', function () {
    if (timer) {
        clearTimeout(timer)
    }
    timer = setTimeout(() => {
        // 模拟触发 change 事件
        console.log(input1.value)

        // 清空定时器
        timer = null
    }, 500)
})

结果:

为了方便起见,我们简单封装一下:

// 防抖
function debounce(fn, delay = 500) {
    // timer 是闭包中的,避免外部修改
    let timer = null

    return function () {
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments)
            timer = null
        }, delay)
    }
}

input1.addEventListener('keyup', debounce(function (e) {
    console.log(e.target)
    console.log(input1.value)
}, 600))

结果:

欢迎打家纠错😂 参考文章