Inspire - Capo Productions
防抖、节流
当持续高频触发事件,如果回调函数很复杂或者有 Ajax 请求,就会导致响应跟不上触发,出现页面卡顿,假死现象。
| 概念 | 描述 | 场景 |
|---|---|---|
| 防抖 | 憋到最后一次 | 按钮防误触 |
| 节流 | 按节奏一直做 | 接口限流 |
防抖
事件频繁触发时,只在“最后一次触发后的一段时间”才执行。
如下图,持续触发 scroll 事件时,并不执行 handle 函数,当 1000 毫秒内没有触发 scroll 事件时,才会延时触发 scroll 事件。

Preview
应用场景
- 搜索框输入查询
- 窗口大小调整
- 按钮点击
实现原理
简版
用于理解防抖函数原理。
js
function debounce(func, wait) {
let timeout
return function (...args) {
clearTimeout(timeout)
timeout = setTimeout(() => {
func.apply(this, args)
}, wait)
}
}
const btn = document.getElementById('btn')
btn.addEventListener('click', debounce(() => {
console.log('点击')
}, 2000))完整版
添加对 this、event 对象的正确解析,以及立刻执行。
js
/**
* @desc 函数防抖
* @param func 函数
* @param wait 延迟执行毫秒数
* @param immediate true 表立即执行,false 表非立即执行
*/
function debounce(func, wait, immediate) {
let timeout
return function () {
const context = this
const args = argments
if (timeoue)
clearTimeOut(timeout)
if (immediate) {
const callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (callNow)
fnc.apply(context, args)
}
else {
timeout = setTimeout(() => {
func.apply(context, args)
}, wait)
}
}
}使用 lodash.debounce
例如,下面是一个带有搜索框的页面,并且需要在用户停止输入 500 毫秒后才开始搜索:
js
import { debounce } from 'lodash-es'
// 根据关键词进行搜索
function search(keyword) {
console.log(`Searching for '${keyword}'...`)
}
const inputEl = document.getElementById('search-input')
// 创建 debounce 函数,最多每 500 毫秒执行一次 search 函数
const debouncedSearch = _.debounce(search, 500)
// 监听 input 变化,如果有变化则调用 debouncedSearch 函数
// 每当出现输入变化时,我们会将输入内容传递给 `debouncedSearch` 函数,这个函数会将搜索操作延迟 500 毫秒后执行。因此,只有用户停止输入 500 毫秒之后,才会真正执行搜索操作
inputEl.addEventListener('input', (evt) => {
const keyword = evt.target.value.trim()
debouncedSearch(keyword)
})节流
事件频繁触发时,每隔一段时间执行一次。
就像水龙头放水,如果阀门开的很大,水会一直流出来,如果阀门开的很小,水会一滴一滴的流出来。
如下图,持续触发 scroll 事件时,并不立即执行 handle 函数,每隔 1000 毫秒才会执行一次 handle 函数。

Preview
应用场景
鼠标移动事件 onmousemove, 滚动滚动条事件 onscroll,窗口大小改变事件 onresize。
实现原理
js
function throttle(fn, wait) {
let lastTime = Date.now()
return function (...args) {
const now = Date.now()
if (now - lastTime > wait) {
func.apply(this, args)
lastTime = now
}
}
}使用 lodash.throttle
例如,下面是一个点击按钮时每隔 1 秒输出一次 log 的示例:
js
function log() {
console.log('Clicked!')
}
const btnEl = document.getElementById('click-button')
// 创建 throttle 函数,最多每 1 秒执行一次 log 函数
const throttledLog = _.throttle(log, 1000)
// 监听 click 事件,如果有点击则调用 throttledLog 函数
// 每当用户点击按钮时,我们会调用 `throttledLog` 函数,这个函数会通过限制函数的执行频率,保证每隔 1 秒钟输出一条日志。即使用户连续点击按钮,也只有第一次点击可以触发函数的执行,后续的点击都会被忽略
btnEl.addEventListener('click', (evt) => {
throttledLog()
})