冷静一下:防抖技术让代码更淡定

226 阅读7分钟

引言

用过搜索框的都知道,实时搜索建议(也称为自动补全或智能提示)是一个常见的功能。它能够根据用户输入的内容,即时显示相关的搜索结果或建议,帮助用户更快地找到他们想要的信息。然而,当用户快速打字时,input 事件会频繁触发,导致短时间内产生大量的网络请求。这不仅增加了服务器的负担,还可能导致页面响应变慢,影响用户体验。为了应对这一挑战,我们可以使用防抖(Debouncing)技术来优化搜索框的行为。

一、什么是防抖?

c0e8c10f6ce0a3fc46d8fb4556819d61.gif

防抖(Debouncing)是一种常见的前端优化技术,用于限制某个函数在短时间内被频繁调用的次数。它确保函数只会在特定的时间间隔之后执行一次,前提是这段时间内没有再次触发相同的事件。防抖对于处理用户输入、窗口调整大小、等事件特别有用,可以有效减少不必要的计算和网络请求,提升性能。

二、防抖的工作原理

防抖技术的核心思想是在事件被触发后等待一定的延迟时间,在这段时间内如果事件再次被触发,则重新开始计时。只有当事件停止触发且经过了设定的延迟时间后,才会执行实际的函数。这种机制就好像让函数等一下,确保了函数不会因为频繁的触发而执行。

image.png

三、百度搜索框中的防抖

image.png 相信你一定认识这个页面:百度一下,你就知道

当用户在搜索框中输入关键词时,搜索引擎需要对用户的输入做出响应,比如提供搜索建议。如果没有防抖技术,每次用户输入一个字符,搜索引擎都会发送一个请求,这无疑会给服务器带来巨大的压力。通过实现防抖技术,百度搜索框会在用户停止输入一定时间后再发送请求,这样既减轻了服务器的负担,也提高了用户体验。

image.png

四、如何实现防抖

下面是一个简单的防抖函数实现:

function debounce(fn,delay){
         let id;       // 定义一个变量用于保存定时器ID
        return function(){
            // 超过500ms 就会执行一次  小于500ms 就会清楚计时器,重新计时
            clearTimeout(id)     // 清除定时器 
            id = setTimeout(()=>{
                fn();   //传入的函数
        },delay)
     }
    }

这段代码定义了一个名为 debounce 的高阶函数,它接受一个函数 fn 和一个延迟时间 delay 作为参数,并返回一个新的函数。这个新函数会在每次被调用时清除之前的定时器,并设置一个新的定时器,只有在指定的延迟时间过后且没有新的调用时,才会执行原函数。

五、代码的工作原理

  1. 定义定时器IDlet id;debounce 函数的作用域中定义了一个变量 id,用来存储定时器的标识符。这使得我们可以稍后通过 clearTimeout(id) 来取消定时器。

  2. 返回的新函数return function() {...} 返回的是一个新的匿名函数,当用户触发相关事件(如输入框的输入事件)时,实际上是这个新函数被调用,而不是直接调用传入的 fn 函数。

  3. 清除旧的定时器clearTimeout(id); 每次新函数被调用时,都会首先尝试清除之前的定时器。这意味着如果在 delay 时间内有新的事件触发,那么之前的计时将被重置,而不会立即执行 fn

  4. 设置新的定时器id = setTimeout(() => { fn(); }, delay); 创建一个新的定时器,它将在 delay 毫秒后执行传入的 fn 函数。只有当在这段时间内没有新的事件触发时,fn 才会被执行。

六、实时搜索建议的防抖应用场景

场景描述

  • 用户在搜索框中输入关键词时,系统需要根据用户的输入实时提供相关的搜索建议或自动补全内容。
  • 如果用户快速打字,input 事件会频繁触发,导致短时间内产生大量的网络请求,增加服务器负担并影响页面响应速度。

解决方案

  • 使用防抖技术,确保只有在用户停止输入一段时间后才发送一次搜索请求。这不仅减少了不必要的网络请求,还提高了系统的性能和用户体验。

示例代码

function debounce(fn, delay) {
    let timeoutId;
    return function(...args) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => {
            fn.apply(this, args);
        }, delay);
    };
}

const searchInput = document.getElementById('search-input');
searchInput.addEventListener('input', debounce(function(event) {
    const query = event.target.value.trim();
    if (query.length > 0) {
        sendSearchRequest(query);
    } else {
        clearSuggestions();
    }
}, 300));

代码解释

  1. 定义防抖函数
function debounce(fn, delay) {
    let timeoutId;
    return function(...args) {
        clearTimeout(timeoutId); // 清除之前的定时器
        timeoutId = setTimeout(() => {
            fn.apply(this, args); // 执行传入的函数
        }, delay);
    };
}
  • debounce(fn, delay):这是一个高阶函数,接受两个参数:

    • fn:要防抖处理的函数。
    • delay:等待的时间间隔(以毫秒为单位),表示在用户停止触发事件后,等待多长时间再执行 fn
  • let timeoutId;:定义一个变量 timeoutId,用于存储定时器的ID。这个变量的作用是保存每次设置的 setTimeout 定时器的引用,以便后续可以清除它。

  • return function(...args)debounce 返回一个新的匿名函数。这个新函数将被绑定到事件监听器上,每当事件触发时,实际上是这个新函数被调用,而不是直接调用 fn

  • clearTimeout(timeoutId);:每次新函数被调用时,首先尝试清除之前的定时器。这意味着如果在 delay 时间内有新的事件触发,那么之前的计时将被重置,而不会立即执行 fn

  • timeoutId = setTimeout(() => { ... }, delay);:设置一个新的定时器,它将在 delay 毫秒后执行传入的 fn 函数。只有当在这段时间内没有新的事件触发时,fn 才会被执行。setTimeout 返回的定时器ID被赋值给 timeoutId,以便后续可以清除它。

  • fn.apply(this, args);:在定时器到期后,使用 apply 方法调用 fn,并将 thisargs 作为参数传递给它。apply 确保 fn 在正确的上下文中执行,并接收所有传递给它的参数。

  1. 应用防抖函数到搜索框
const searchInput = document.getElementById('search-input');
searchInput.addEventListener('input', debounce(function(event) {
    const query = event.target.value.trim();
    if (query.length > 0) {
        sendSearchRequest(query);
    } else {
        clearSuggestions();
    }
}, 300));
  • const searchInput = document.getElementById('search-input');:获取页面中ID为 search-input 的输入框元素。假设这是一个文本输入框,用户可以在其中输入搜索关键词。

  • searchInput.addEventListener('input', ...):为 searchInput 绑定 input 事件监听器。input 事件会在用户每次在输入框中输入字符时触发。

  • debounce(function(event) { ... }, 300):将防抖函数应用于 input 事件。这里我们传入了一个匿名函数作为 fn,并设置了 delay 为 300 毫秒。这意味着只有当用户停止输入 300 毫秒后,才会执行这个匿名函数。

  • 匿名函数的内容

    • const query = event.target.value.trim();:获取用户在输入框中的当前值,并使用 trim() 去除首尾的空白字符。event.target 是触发事件的DOM元素,即 searchInput

    • if (query.length > 0) { ... }:检查 query 是否为空。如果 query 非空,则调用 sendSearchRequest(query) 发送搜索请求。

    • else { clearSuggestions(); }:如果 query 为空(即用户清空了输入框),则调用 clearSuggestions() 清除之前显示的搜索建议。

七、防抖与节流的区别

防抖和节流(Throttling)都是用来控制函数执行频率的技术,但它们的目的和实现方式有所不同:

  • 防抖:确保函数在事件触发后的一段时间内只执行一次,适合用于搜索建议等场景。
  • 节流:确保函数在固定的时间内最多执行一次,适合用于滚动事件等需要定期执行的场景。

结语

防抖技术教会我们耐心和等待,这在快节奏的数字世界中是一种宝贵的品质。它提醒我们,有时候,慢下来,是为了更快地到达目的地。在未来的开发旅程中,让我们继续探索和应用这些巧妙的技术,让代码不仅跑得快,而且跑得优雅。

image.png