前端项目开发过程中,对一个dom元素动作绑定了事件,但触发dom函数的动作过于频繁从而影响页面性能甚至出现bug的情况,比如:
页面滚动scroll事件、浏览器窗口resize事件、输入框搜索input事件等等,这些事件如果在一段时间内不加限制频繁触发必定会导致页面性能变差,尤其是绑定的事件内包含触发页面重绘重排、ajax请求这类操作时,甚至可能出现卡顿、假死、数值错误之类的bug。。。
所以为了防止绑定事件多次触发,就需要考虑使用防抖和节流的方案了
防抖:事件在指定时间间隔后触发,在时间间隔内如果重复触发事件就重新计时,多用于取多次操作的最后一次操作为有效操作的场景如:搜索框输入搜索、点击切换状态按钮、表单数据提交
节流:事件在指定事件间隔内只触发一次,时间间隔内重复触发则只有第一次生效,超出时间间隔直接执行,多用于多次操作会取得相同结果从而避免多次触发的情况如:滚动加载、新增列表项、拖拽元素
防抖和节流在代码中如何实现:
防抖
1 /** 2 * @param {*} fn 要进行防抖的函数 3 * @param {*} delay 延迟几秒执行 4 * @param {*} immediate 是否立即执行一次防抖函数(fn) 5 */ 6 function debounce(fn, delay, immediate) { 7 let timeout = null 8 return function(...args) { 9 // 清除已存在定时任务 10 if(timeout) { 11 clearTimeout(timeout) 12 timeout = null 13 } else { 14 if(immediate) fn.apply(this, args) 15 } 16 // 重新设置定时任务 17 timeout = setTimeout(() => { 18 fn.apply(this, ...args) 19 }, delay) 20 } 21 }
节流
1 /** 2 * 立即执行版本 3 * @param {*} fn 要执行节流的函数 4 * @param {*} wait 节流函数执行周期 5 */ 6 function throttle(fn, wait) { 7 let pre = Date.now() 8 return function(...args) { 9 let now = Date.now() 10 if(now - pre > wait) { 11 fn.apply(this, args) 12 pre = now 13 } 14 } 15 } 16 17 /** 18 * 延迟指定时间执行版本 19 * @param {*} fn 要执行节流的函数 20 * @param {*} wait 节流函数执行周期 21 */ 22 function throttle(fn, wait) { 23 let timeout = null 24 return function(...args) { 25 if(!timeout) { 26 setTimeout(() => { 27 fn.apply(this, args) 28 timeout = null 29 }, wait) 30 } 31 } 32 }
最后我们来看一下使用了防抖和节流后的效果:
防抖:
这里通过在控制台输出input框内容模拟实际应用场景中用户手动输入搜索,可以看到没有使用防抖函数,用户每次输入都会触发搜索操作,非常损耗性能
使用防抖后(这里的防抖延迟我设定的是1s),可以看到只有当用户输入间隔超过1s后才会真正执行搜索逻辑,大大节省了性能
节流:
通过页面滚动条滚动到指定高度时触发控制台输出不同内容模拟滚动加载,可以看到在不使用节流的情况下,滚动条滚动到指定高度后继续滚动还会触发加载函数
而使用了节流后,即使多次滚动也只会触发一次加载函数,大大节省了性能