JS中的函数防抖
一、什么是函数防抖
概念:函数防抖(debounce),就是指触发事件后,在 n 秒内函数只能执行一次,如果触发事件后在 n 秒内又触发了事件,则会重新计算函数延执行时间。
二、为什么需要函数防抖
前端开发过程中,有一些事件,常见的例如,onresize,scroll,mousemove ,mousehover 等,会被频繁触发(短时间内多次触发),不做限制的话,有可能一秒之内执行几十次、几百次,如果在这些函数内部执行了其他函数,尤其是执行了操作 DOM 的函数(浏览器操作 DOM 是很耗费性能的),那不仅会浪费计算机资源,还会降低程序运行速度,甚至造成浏览器卡死、崩溃。这种问题显然是致命的。
三、函数防抖如何解决上述问题
根据上面对问题的分析,细细思索,可以想到如下解决方案。
函数防抖的要点,是需要一个 setTimeout 来辅助实现,延迟运行需要执行的代码。如果方法多次触发,则把上次记录的延迟执行代码用 clearTimeout 清掉,重新开始计时。若计时期间事件没有被重新触发,等延迟时间计时完毕,则执行目标代码。
四、函数防抖的代码实现
根据以上分析,我们对 “函数防抖” 来进行简单的代码实现,如下:
function debounce(fn,wait){ var timer = null; return function(){ if(timer !== null){ clearTimeout(timer); } timer = setTimeout(fn,wait); } } function handle(){ console.log(Math.random()); } window.addEventListener("resize",debounce(handle,1000));
防抖和节流
Vue 没有内置支持防抖和节流,但可以使用 Lodash
等库来实现。
如果某个组件仅使用一次,可以在 methods
中直接应用防抖:
<script src="https://unpkg.com/lodash@4.17.20/lodash.min.js"></script> <script> Vue.createApp({ methods: { // 用 Lodash 的防抖函数 click: _.debounce(function() { // ... 响应点击 ... }, 500) } }).mount('#app') </script>
但是,这种方法对于可复用组件有潜在的问题,因为它们都共享相同的防抖函数。为了使组件实例彼此独立,可以在生命周期钩子的 created
里添加该防抖函数:
app.component('save-button', { created() { // 用 Lodash 的防抖函数 this.debouncedClick = _.debounce(this.click, 500) }, unmounted() { // 移除组件时,取消定时器 this.debouncedClick.cancel() }, methods: { click() { // ... 响应点击 ... } }, template: ` <button @click="debouncedClick"> Save </button> ` })
五、函数节流的使用场景
函数防抖一般用在什么情况之下呢?一般用在,连续的事件只需触发一次回调的场合。具体有:
- 搜索框搜索输入。只需用户最后一次输入完,再发送请求;
- 用户名、手机号、邮箱输入验证;
- 浏览器窗口大小改变后,只需窗口调整完后,再执行 resize 事件中的代码,防止重复渲染。
目前遇到过的用处就是这些,理解了原理与实现思路,小伙伴可以把它运用在任何需要的场合,提高代码质量。
总结
函数防抖其实是分为 “立即执行版” 和 “非立即执行版” 的,根据字面意思就可以发现他们的差别,所谓立即执行版就是 触发事件后函数不会立即执行,而是在 n 秒后执行,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。 而 “非立即执行版” 指的是 触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果。。
在开发过程中,我们需要根据不同的场景来决定我们需要使用哪一个版本的防抖函数,一般来讲上述的防抖函数都能满足大部分的场景需求。但我们也可以将非立即执行版和立即执行版的防抖函数结合起来,实现最终的双剑合璧版本的防抖函数,以下为小伙伴们做了简单的实现:
/** * @desc 函数防抖---“立即执行版本” 和 “非立即执行版本” 的组合版本 * @param func 需要执行的函数 * @param wait 延迟执行时间(毫秒) * @param immediate---true 表立即执行,false 表非立即执行 **/ function debounce(func, wait, immediate) { let timer; return function() { let context = this; let args = arguments; if (timer) clearTimeout(timer); if (immediate) { var callNow = !timer; timer = setTimeout(() => { timer = null; }, wait) if (callNow) func.apply(context, args) } else { timer = setTimeout(function() { func.apply(context, args) }, wait); } } } function handle() { console.log(Math.random()); } // window.addEventListener("mousemove",debounce(handle,1000,true)); // 调用立即执行版本 window.addEventListener("mousemove", debounce(handle, 1000, false)); // 调用非立即执行版本
本文参考:
https://segmentfault.com/a/1190000019591549
https://vue3js.cn/docs/zh/guide/data-methods.html#%E9%98%B2%E6%8A%96%E5%92%8C%E8%8A%82%E6%B5%81