admin管理员组文章数量:1794759
vue监听scroll使用节流函数(throttle)或防抖函数(debounce)遇到的坑
在浏览器 DOM 事件里面,有一些事件会随着用户的操作不间断触发。比如:重新调整浏览器窗口大小(resize),浏览器页面滚动(scroll),鼠标移动(mousemove)、文字输入(keyup)、 射击游戏中(mousedown、keydown)。也就是说用户在触发这些浏览器操作的时候,如果脚本里面绑定了对应的事件处理方法,这个方法就不停的触发。
在项目中需要通过监听元素的scroll来实现页面返回顶部,可以通过@scroll.passive实现监听滚动,但是只要滚定页面,就会不断的触发事件,这不是我想要的效果,也会影响系统的性能,所以选择防抖或者节流函数。本文的目的不是讲解防抖或者节流函数,网上已经有太大的文章了,主要讲的是在vue中使用throttle或者debounce遇到的坑。
div元素监听scroll:
<div class="cfpa-brief-preview-scroll" ref="previewText" @scroll.passive="fnScroll"></div>节流函数:
/** * @description [throttle 节流函数] * @author shanshuizinong * @param {Function} fn 延时调用函数 * @param {Number} delay 延迟多长时间 * @param {Number} atleast 至少多长时间触发一次 * @return {Function} 延迟执行的方法 */ export function throttle (fn, delay, atleast) { let timer = null let previous = null return function () { let now = +new Date() if (!previous) previous = now if (atleast && now - previous > atleast) { fn() previous = now clearTimeout(timer) } else { clearTimeout(timer) timer = setTimeout(function () { fn() previous = null }, delay) } } }vue代码(节流函数执行异常代码):
<template> <div class="cfpa-brief-preview-scroll" ref="previewText" @scroll.passive="fnScroll"> // 此处省略html代码 </div> </template> <script> export default { name: 'publicPreviewBriefing', data () { return { count: 0 } }, methods: { /** * @description [fnScroll 滚动执行函数] * @author shanshuizinong * @returns {null} [没有返回] */ fnScroll () { this.fnThrottle(this.fnHandleScroll, 2000)() }, /** * @description [fnHandleScroll 内容区域滚动] * @author shanshuizinong * @param {Object} e [事件对象] * @returns {null} [没有返回] */ fnHandleScroll (e) { console.log('scroll触发了:' + this.count++, new Date()) }, /** * @description [fnThrottle 节流函数] * @author shanshuizinong * @param {Function} fn 延时调用函数 * @param {Number} delay 延迟多长时间 * @param {Number} atleast 至少多长时间触发一次 * @return {Function} 延迟执行的方法 */ fnThrottle (fn, delay, atleast) { let timer = null let previous = null return function () { let now = +new Date() if (!previous) previous = now if (atleast && now - previous > atleast) { fn() previous = now clearTimeout(timer) } else { clearTimeout(timer) timer = setTimeout(function () { fn() previous = null }, delay) } } } } } </script>这样确实实现了第1次节流,fnHandleScroll也会在2s后调用,但是一次性会执行很多次fnHandleScroll函数,如下图: 在1秒之内执行了11次,显然这不是我想要的效果,我要的效果是停止滚动2s后执行一次fnHandleScroll函数。解决办法把fnScroll函数从method方法中移入到data属性中,然后在created生命周期给fnScroll赋值,可以完美解决这个bug。
<template> <div class="cfpa-brief-preview-scroll" ref="previewText" @scroll.passive="fnScroll"> // 此处省略html代码 </div> </template> <script> export default { name: 'publicPreviewBriefing', data () { return { count: 0, fnScroll: () => {} } }, created () { this.fnScroll = this.fnThrottle(this.fnHandleScroll, 2000) }, methods: { /** * @description [fnHandleScroll 内容区域滚动] * @author shanshuizinong * @param {Object} e [事件对象] * @returns {null} [没有返回] */ fnHandleScroll (e) { console.log('scroll触发了:' + this.count++, new Date()) }, /** * @description [fnThrottle 节流函数] * @author shanshuizinong * @param {Function} fn 延时调用函数 * @param {Number} delay 延迟多长时间 * @param {Number} atleast 至少多长时间触发一次 * @return {Function} 延迟执行的方法 */ fnThrottle (fn, delay, atleast) { let timer = null let previous = null return function () { let now = +new Date() if (!previous) previous = now if (atleast && now - previous > atleast) { fn() previous = now clearTimeout(timer) } else { clearTimeout(timer) timer = setTimeout(function () { fn() previous = null }, delay) } } } } } </script>通过触发的时间可以看出,fnHandleScroll不会连续执行多次,基本达到了预期效果,解决这个问题还有其他的方法,我这里只提供这一种,fnThrottle 函数还可以提供第三个参数,希望至少多长时间触发一次。
参考文献: JavaScript 节流函数 Throttle 详解
vue 函数节流中匿名函数问题
版权声明:本文标题:vue监听scroll使用节流函数(throttle)或防抖函数(debounce)遇到的坑 内容由林淑君副主任自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.xiehuijuan.com/baike/1686492796a73628.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论