结合Google Suggestion带你实现防抖节流

前端防抖节流

在现代Web开发中,用户体验和服务器性能是两个非常重要的方面。频繁的用户操作,如输入搜索内容、滚动页面等,可能会导致大量的请求被发送到服务器,从而增加服务器的负担。为了解决这个问题,前端开发中常用防抖(Debounce)技术。本文将详细探讨防抖和节流的概念、实现方式及其在实际开发中的应用。

Google Suggestion

相信大家在日常搜索信息时都体验过当你每输入一个字符,都会弹出相关联的信息。其实用户在输入搜索内容时,前端是会向后端发送请求以获取匹配的数据。然而,如果每次输入一个字符就发送一次请求,会导致服务器压力剧增,更别提每天有成千上万的用户在进行信息搜索。因此,我们需要控制请求的频率,这就引入了防抖的概念。

事件处理与防抖

模拟后端数据

首先我们用json-serve来模拟一下后端数据

Bash 复制代码
npm init -y 
npm install json-server

项目初始化完毕后,在package.json中添加脚本:

json 复制代码
"scripts": {
    "dev": "json-server --watch db.json"
}

然后创建db.json文件:

json 复制代码
{
  "users": [
    {
      "id": "1",
      "name": "jesper"
    },
    {
      "id": "2",
      "name": "kim"
    },
    {
      "id": "3",
      "name": "jimmy"
    }
  ]
}

最后运行json-server

bash 复制代码
npm run dev

这会启动一个本地服务器,例如可以通过http://localhost:3000/users/1访问用户id为1的数据。我这边默认是3000端口,你也可以用--port指定端口。

事件触发与Ajax请求

在用户每次输入一个字符时,都会触发一个keyup事件。所以我们拿到输入框的DOM节点并监听keyup事件,再写一个事件处理函数拿到与输入字符相关的数据

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>防抖示例</title>
</head>
<body>
    <div>
        没有防抖的输入框
        <input type="text" id="unDebounce" placeholder="请输入您要搜索的用户名">
    </div>
    <script>
        // 不加防抖的输入框
        const input = document.getElementById('unDebounce');

        function handleNameSearch(e) {
            const value = e.target.value;
            fetch("http://localhost:3000/users")
                .then(res => res.json())
                .then(data => {
                    const users = data;
                    const filterUsers = users.filter(user => {
                        return user.name.includes(value);
                    });
                    console.log(filterUsers);
                });
        }

        input.addEventListener("keyup", handleNameSearch);
    </script>
</body>
</html>

以上就是一个没有防抖的输入框,效果就是每次敲击键盘,前端都会向后端请求数据,性能开销很大。

防抖(Debounce)

防抖是指在连续事件触发后,只在事件结束的一段时间后执行一次回调函数。也就是说,如果在这段时间内事件再次触发,计时器会被重置,直到事件不再触发,才会执行回调函数。

防抖函数实现

以下是一个使用防抖技术优化用户输入搜索的示例:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>防抖示例</title>
</head>
<body>
    <div>
        防抖后的输入框
        <input type="text" id="Debounce" placeholder="请输入您要搜索的用户名">
    </div>
    <script>
        // 加防抖的输入框
        const input2 = document.getElementById('Debounce');

        function handleNameSearch(e) {
            const value = e.target.value;
            fetch("http://localhost:3000/users")
                .then(res => res.json())
                .then(data => {
                    const users = data;
                    const filterUsers = users.filter(user => {
                        return user.name.includes(value);
                    });
                    console.log(filterUsers);
                });
        }

        // 防抖函数
        function debounce(fn, delay) {
            return function(args) {
                clearTimeout(fn.id);
                fn.id = setTimeout(() => {
                    fn(args);
                }, delay);
            }
        }

        const debounceNameSearch = debounce(handleNameSearch, 500);
        input2.addEventListener("keyup", debounceNameSearch);
    </script>
</body>
</html>

防抖原理

防抖函数通过使用定时器来延迟函数的执行。如果在定时器计时结束之前再次触发事件,定时器会被重置。这样可以确保只有在一段时间内没有新的事件触发时,回调函数才会被执行。

在上面的示例中,可以看到主要变化就是我们使用了一个防抖函数debounce来包裹handleNameSearch,并将防抖后的函数绑定到输入框的keyup事件上。这样,只有在用户停止输入500毫秒后,才会发送请求。

所以我们这边主要来解毒一下防抖函数:

  1. 这个防抖函数接收两个参数:fn是需要防抖的函数,delay是延迟时间。
  2. 用一个定时器延缓函数fn的执行,并用fn.id保存setTimeout返回的定时器ID,以便之后可以通过这个ID取消定时器(这里也可以利用闭包)
  3. 如果用户的输入在距上次输入时间小于delay,则会取消上一次的定时器,也就取消了函数的执行
  4. 所以就达成了只有之后没有输入或两次输入超过delay时间才会执行函数的效果

防抖的应用场景

防抖技术在以下场景中非常有用:

  1. 搜索输入框:当用户在搜索框中输入内容时,可以使用防抖技术减少请求次数,降低服务器压力。
  2. 窗口大小调整:当用户调整浏览器窗口大小时,可以使用防抖技术减少resize事件处理函数的调用次数。
  3. 表单验证:在表单输入框中进行实时验证时,可以使用防抖技术减少验证函数的调用次数。

节流(Throttle)

节流是指在连续事件触发时,保证一定时间段内只调用一次回调函数。与防抖不同,节流是在一定时间内周期性地执行回调函数,而不是在事件结束后才执行。

节流函数实现

由上面的防抖可知,防抖节流的主要实现方式就是写一个防抖节流函数,运行结果为返回一个函数,使通过传参进来的函数按照我们自己制定的规则来触发。所以下面我们直接给出节流函数。

以下是一个简单的节流函数示例:

js 复制代码
 const throttle = (func,delay) => {
            //last 上一次是什么时候执行的 deferTimer 定时器id
            let last,deferTimer
            return (args) => {
                //当前时间
                let now = +new Date() 
                if(last && now < last + delay){
                    clearTimeout(deferTimer);   
                    deferTimer = setTimeout(() => {
                        last = now;
                        func(args);
                    },delay)
                }else{
                    last = now
                    func(args)
                }
            }
        }

节流原理

节流函数代码量比防抖多了一些,不着急,我们先来看下面更简单的代码

js 复制代码
const throttle = (func,delay) => {
            //last 上一次是什么时候执行的
            let last
            return (args) => {
                //当前时间
                let now = +new Date() 
                if(last && now < last + delay){
                    
                }else{
                    last = now
                    func(args)
                }
            }
        }

可以看到我们把if中的内容全挖空了,大家觉得上面的代码能实现节流的效果吗?

我们来分析一下:

  1. 首先函数接收两个参数,fn是需要防抖的函数,delay是延迟时间。然后定义了一个变量last用来记录函数上次执行的时间。
  2. 定义了一个变量now来记录当前时间,并用+进行隐式类型转换,将date对象转换为数字类型,便于计算。
  3. 第一次触发,last没有值,进入else执行func并记录执行时间。
  4. 接下来的触发中,不断刷新now值判断现在距离上次执行函数有没有超过时间delay。没有超过则进入if,什么都不做,反之进入else,并重复执行上述步骤。

这么看来,这样不是可以实现吗,但其实我们漏掉了一种特殊情况,就是第一次触发后,现在距离上次执行函数有没有超过delay的时间内用户一直触发,但就在间隔要超过delay的前一刻,用户又不触发了,那结果会是什么,函数不执行,那用户在这段时间的输入不是就无效了吗,所以我们还得保证最后一次要执行。什么?保证最后一次要执行?这不就是防抖吗,所以我们直接把防抖的内容放到节流函数的if中,并给last赋值,就完成了节流函数。

js 复制代码
const throttle = (func,delay) => {
            //last 上一次是什么时候执行的 deferTimer 定时器id
            let last,deferTimer
            return (args) => {
                //当前时间
                let now = +new Date() 
                if(last && now < last + delay){
                    clearTimeout(deferTimer);   
                    deferTimer = setTimeout(() => {
                        last = now;
                        func(args);
                    },delay)
                }else{
                    last = now
                    func(args)
                }
            }
        }

节流的应用场景

  1. 滚动事件(scroll):例如在实现图片懒加载时,监听滚动事件最好加上节流。
  2. 鼠标移动(mousemove) :当用户在页面上移动鼠标时,mousemove 事件会频繁触发。如果每次都执行回调,可能会导致页面性能下降。节流可以控制鼠标事件触发的频率。

总结

  • 防抖适用于需要等待用户操作结束之后才执行某个动作的场景(例如输入框的自动完成、表单验证)。

  • 节流适用于需要限制事件执行频率、减少频繁执行导致性能问题的场景(例如滚动、鼠标移动、API 请求)。

相关推荐
蓝天白云下遛狗21 分钟前
goole chrome变更默认搜索引擎为百度
前端·chrome
come1123444 分钟前
Vue 响应式数据传递:ref、reactive 与 Provide/Inject 完全指南
前端·javascript·vue.js
前端风云志1 小时前
TypeScript结构化类型初探
javascript
musk12121 小时前
electron 打包太大 试试 tauri , tauri 安装打包demo
前端·electron·tauri
翻滚吧键盘2 小时前
js代码09
开发语言·javascript·ecmascript
万少2 小时前
第五款 HarmonyOS 上架作品 奇趣故事匣 来了
前端·harmonyos·客户端
OpenGL2 小时前
Android targetSdkVersion升级至35(Android15)相关问题
前端
rzl023 小时前
java web5(黑马)
java·开发语言·前端
Amy.Wang3 小时前
前端如何实现电子签名
前端·javascript·html5
海天胜景3 小时前
vue3 el-table 行筛选 设置为单选
javascript·vue.js·elementui