防抖与节流

May 26, 2024· 7 min read

概述

防抖和节流是前端开发中常用的优化手段,它们主要作用就是限制函数的执行频率,提高性能和用户体验,主要用于处理高频触发的事件,例如:鼠标滚动,输入框输入,重复点击等。

防抖与节流的区别

先简单介绍下两者。

防抖:主要应用于限制某个函数在短时间内的连续执行次数。如果函数持续地、频繁地被触发,防抖技术会确保该函数在一定时间间隔内只执行一次。

节流:用于控制某个函数在单位时间内的执行频率。与防抖不同,节流不会取消函数执行,而是保证函数在固定的时间间隔内执行。当连续频繁地触发函数时,节流可以降低函数的执行频率,提高性能。

理解:如果你玩过 Data 类游戏的话,在游戏中节流就相当于释放了一个技能后,技能进入了冷却 CD,在 CD 冷却完成之前是没法再次释放技能的,而防抖就是你一直按回城技能,只会以最后一次为准开始计时。(现在是云玩家了,印象中是这样的机制)

简而言之,防抖是在事件停止触发后延迟执行函数,而节流是按照固定的时间间隔执行函数。

防抖的实现与使用

应用场景:

  1. 搜索框实时搜索:用户在搜索框输入文字时,我们可以使用防抖技术减少搜索请求的次数,只有在用户连续输入完成后才发出搜索请求。
  2. Window 的 resize 事件:在 window 尺寸变化时,我们可能需要进行一些计算或者其他操作,使用防抖可以降低触发这些操作的频率。
  3. 表单的验证等:在用户输入时进行数据验证,无需每一次输入都触发验证,而是在用户结束输入一段时间之后再进行验证。

实现原理:实现防抖基本方式就是利用定时器,每次触发事件清除上次定时器重新开始计时。

首先先实现一个 js 基础版本的

JSX
function debounce(fn, wait = 300) { let timeout = null; return function () { const context = this; clearTimeout(timeout); timeout = setTimeout(() => { fn.apply(context, arguments); }, wait); }; }

如果我们的使用场景是按钮点击,这时就会出现一个问题。我们只点击了一次,但却需要等待一段时间(取决于定时器的延迟)才会执行相应的点击事件函数。因此,我们需要为防抖函数增加一个参数来决定是否立即执行。

JSX
function debounce(fn, wait = 300, immediate = false) { let timeout = null; return function () { const context = this; const args = arguments; const later = () => { timeout = null; if (!immediate) fn.apply(context, args); }; const shouldCallNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (shouldCallNow) { fn.apply(context, args); } }; }

使用前:

使用后:

Typescript 版本:

TSX
function debounce<T extends (...args: any[]) => any>( func: T, wait = 300, immediate = false ): (...args: Parameters<T>) => void { let timeout: ReturnType<typeof setTimeout> | null; return function (this: ThisParameterType<T>, ...args: Parameters<T>) { const doLater = () => { timeoutId = undefined; if (!immediate) { func.apply(this, args); } }; const shouldCallNow = immediate && !timeoutId; if (timeoutId !== null) { clearTimeout(timeoutId); } timeoutId = setTimeout(doLater, wait); if (shouldCallNow) { func.apply(this, args); } }; }

节流函数的实现与使用

节流主要应用场景如下:

  1. 滚动事件:监听滚动事件并执行相应操作时,可以使用节流来减少操作的执行频率,提高性能。
  2. 鼠标移动事件:当鼠标在页面上移动时,我们可能需要根据鼠标位置实时执行一些操作,使用节流可以降低这些操作的执行频率。

实现原理:如果未定义定时器就创建定时器,执行完定时器回调函数时再将定时器清空并执行真正的 handler

JSX
function throttle(fn, wait = 300, immediate = false) { let timeout = null; return function () { let context = this; let args = arguments; const shouldCallNow = immediate && !timeout; if (!timeout) { timeout = setTimeout(() => { timeout = null; clearTimeout(timeout); if (!immediate) { fn.apply(context, args); } context = args = null; }, wait); } if (shouldCallNow) { fn.apply(context, args); } }; }

使用前:

使用后:

Typescript 版本

TSX
function throttle<T extends (...args: any[]) => any>( fn: T, wait = 300, immediate = false ): (...args: Parameters<T>) => void { let timeout: ReturnType<typeof setTimeout> | null = null; return function (this: ThisParameterType<T>, ...args: Parameters<T>) { const later = () => { timeout = null; if (!immediate) { fn.apply(this, args); } }; const shouldCallNow = immediate && !timeout; if (!timeout) { timeout = setTimeout(later, wait); } if (shouldCallNow) { fn.apply(this, args); } }; }

总结

防抖和节流是优化高频率事件的重要技术,能够提升网页的性能和用户体验。防抖是通过延迟函数执行来避免函数的频繁执行,而节流则是在一定时间间隔内限制函数的执行次数。将这两种技术应用到实际开发中,能有效地控制事件的触发频率,减少不必要的计算和渲染,提升页面性能。