摘要:节流和防抖在开发项目过程中很常见,例如输入实时搜索滚动更新了,等等,大量的场景需要我们对其进行处理。防抖多次触发,只在最后一次触发时,执行目标函数。节流限制目标函数调用的频率,比如内不能调用次。
节流和防抖在开发项目过程中很常见,例如 input 输入实时搜索、scrollview 滚动更新了,等等,大量的场景需要我们对其进行处理。我们由 Lodash 来介绍,直接进入主题吧。
Lodash API
防抖 (debounce) :多次触发,只在最后一次触发时,执行目标函数。
lodash.debounce(func, [wait=0], [options={}])
节流(throttle):限制目标函数调用的频率,比如:1s内不能调用2次。
lodash.throttle(func, [wait=0], [options={}])
lodash 在 opitons 参数中定义了一些选项,主要是以下三个:
leading:函数在每个等待时延的开始被调用,默认值为false
trailing:函数在每个等待时延的结束被调用,默认值是true
maxwait:最大的等待时间,因为如果 debounce 的函数调用时间不满足条件,可能永远都无法触发,因此增加了这个配置,保证大于一段时间后一定能执行一次函数
根据 leading 和 trailing 的组合,可以实现不同的调用效果:
{leading: true, trailing: false}:只在延时开始时调用
{leading: false, trailing: true}:默认情况,即在延时结束后才会调用函数
{leading: true, trailing: true}:在延时开始时就调用,延时结束后也会调用
deboucne 还有 cancel 方法,用于取消防抖动调用
使用
防抖 (debounce):
addEntity = () => { console.log("--------------addEntity---------------") this.debounceFun(); } debounceFun = lodash.debounce(function(e){ console.log("--------------debounceFun---------------"); }, 500,{ leading: true, trailing: false, })
首次点击时执行,连续点击且时间间隔在500ms之内,不再执行,间隔在500ms之外再次点击,执行。
节流(throttle):
addEntity = () => { console.log("--------------addEntity---------------"); this.throttleFun(); } throttleFun = lodash.throttle(function(e){ console.log("--------------throttleFun---------------"); }, 500,{ leading: true, trailing: false, })
首次点击时执行,连续点击且间隔在500ms之内,500ms之后自动执行一次(注:连续点击次数时间之后小于500ms,则不会自动执行),间隔在500ms之外再次点击,执行。
源码实现 debounce// 这个是用来获取当前时间戳的 function now() { return +new Date() } /** * 防抖函数,返回函数连续调用时,空闲时间必须大于或等于 wait,func 才会执行 * * @param {function} func 回调函数 * @param {number} wait 表示时间窗口的间隔 * @param {boolean} immediate 设置为ture时,是否立即调用函数 * @return {function} 返回客户调用函数 */ function debounce (func, wait = 50, immediate = true) { let timer, context, args // 延迟执行函数 const later = () => setTimeout(() => { // 延迟函数执行完毕,清空缓存的定时器序号 timer = null // 延迟执行的情况下,函数会在延迟函数中执行 // 使用到之前缓存的参数和上下文 if (!immediate) { func.apply(context, args) context = args = null } }, wait) // 这里返回的函数是每次实际调用的函数 return function(...params) { // 如果没有创建延迟执行函数(later),就创建一个 if (!timer) { timer = later() // 如果是立即执行,调用函数 // 否则缓存参数和调用上下文 if (immediate) { func.apply(this, params) } else { context = this args = params } // 如果已有延迟执行函数(later),调用的时候清除原来的并重新设定一个 // 这样做延迟函数会重新计时 } else { clearTimeout(timer) timer = later() } } }throttle
/** * underscore 节流函数,返回函数连续调用时,func 执行频率限定为 次 / wait * * @param {function} func 回调函数 * @param {number} wait 表示时间窗口的间隔 * @param {object} options 如果想忽略开始函数的的调用,传入{leading: false}。 * 如果想忽略结尾函数的调用,传入{trailing: false} * 两者不能共存,否则函数不能执行 * @return {function} 返回客户调用函数 */ _.throttle = function(func, wait, options) { var context, args, result; var timeout = null; // 之前的时间戳 var previous = 0; // 如果 options 没传则设为空对象 if (!options) options = {}; // 定时器回调函数 var later = function() { // 如果设置了 leading,就将 previous 设为 0 // 用于下面函数的第一个 if 判断 previous = options.leading === false ? 0 : _.now(); // 置空一是为了防止内存泄漏,二是为了下面的定时器判断 timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; return function() { // 获得当前时间戳 var now = _.now(); // 首次进入前者肯定为 true // 如果需要第一次不执行函数 // 就将上次时间戳设为当前的 // 这样在接下来计算 remaining 的值时会大于0 if (!previous && options.leading === false) previous = now; // 计算剩余时间 var remaining = wait - (now - previous); context = this; args = arguments; // 如果当前调用已经大于上次调用时间 + wait // 或者用户手动调了时间 // 如果设置了 trailing,只会进入这个条件 // 如果没有设置 leading,那么第一次会进入这个条件 // 还有一点,你可能会觉得开启了定时器那么应该不会进入这个 if 条件了 // 其实还是会进入的,因为定时器的延时 // 并不是准确的时间,很可能你设置了2秒 // 但是他需要2.2秒才触发,这时候就会进入这个条件 if (remaining <= 0 || remaining > wait) { // 如果存在定时器就清理掉否则会调用二次回调 if (timeout) { clearTimeout(timeout); timeout = null; } previous = now; result = func.apply(context, args); if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { // 判断是否设置了定时器和 trailing // 没有的话就开启一个定时器 // 并且不能不能同时设置 leading 和 trailing timeout = setTimeout(later, remaining); } return result; }; };
走在最后,欢迎 star:https://github.com/sisterAn/blog
欢迎关注:前端瓶子君
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/105925.html
摘要:个人博客原文地址背景我们在开发的过程中会经常使用如等事件,如果正常绑定事件处理函数的话,有可能在很短的时间内多次连续触发事件,十分影响性能。 个人博客原文地址 背景 我们在开发的过程中会经常使用如scroll、resize、touchmove等事件,如果正常绑定事件处理函数的话,有可能在很短的时间内多次连续触发事件,十分影响性能。因此针对这类事件要进行节流或者防抖处理 节流 节流的意思...
摘要:首先重置防抖函数最后调用时间,然后去触发一个定时器,保证后接下来的执行。这就避免了手动管理定时器。 之前遇到过一个场景,页面上有几个d3.js绘制的图形。如果调整浏览器可视区大小,会引发图形重绘。当图中的节点比较多的时候,页面会显得异常卡顿。为了限制类似于这种短时间内高频率触发的情况,我们可以使用防抖函数。 实际开发过程中,这样的情况其实很多,比如: 页面的scroll事件 ...
摘要:您的支持是我最大的动力,我会保证提供高质与清晰的文章与您共同成长。一些文章中的与上面所谈到的设置类似。防抖防抖技术允许我们捆绑多个连续调用成为单一的一次调用。防抖的应用这个简单的举个 欢迎star和watch我的github issue blog,欢迎加入讨论。您的支持是我最大的动力,我会保证提供高质与清晰的文章与您共同成长。 节流[throttle]与防抖[debounce]在前...
摘要:节流分流,与防抖去抖实现原理相似。本文主要讨论节流,镜像文章防抖理解,实践与实现。分开讨论防抖和节流,主要是为了让一些还不太了解节流防抖的读者能够有针对性地,逐一掌握它们。上方为未节流模式,每一次触发都会绘制一个圆点。 showImg(https://segmentfault.com/img/bVbqMwN?w=1280&h=720); 节流(分流),与防抖(去抖)实现原理相似。本文主...
阅读 2060·2021-09-22 15:43
阅读 8615·2021-09-22 15:07
阅读 1077·2021-09-03 10:28
阅读 2051·2021-08-19 10:57
阅读 1060·2020-01-08 12:18
阅读 2971·2019-08-29 15:09
阅读 1520·2019-08-29 14:05
阅读 1639·2019-08-29 13:57