likes
comments
collection
share

防抖节流,必须必拿下!

作者站长头像
站长
· 阅读数 19

一、前言

之前我和大家分享过了图片懒加载,在最后需要解决滚动方法调用频率时,我们留了个后手,那今天就一起来看看什么是防抖节流,并且探究为什么它们能让面试官屡次提问。

防抖节流,必须必拿下!(❁´◡`❁)

二、认识防抖和节流

防抖和节流其实是两种在前端开发中常见的优化技术,主要用于处理高频触发的事件,如窗口的resize、scroll事件,输入框的input事件等,并不是只有图片懒加载需要用到。它们的目的都是为了减少不必要的函数调用,提高程序的性能,缓解服务器压力,毕竟一个请求就是一个并发,而总连接数是有上限的。

它们的基本实现思想是这样的:

  • 防抖(Debounce) :防抖的原理是在事件被触发后,在指定的时间内如果事件没有再次被触发,则执行一次函数;如果在这段时间内事件再次被触发,则重新计时。简单来说,就是将多次触发的事件合并为一次执行,常用于需要等待用户操作完全停止后再进行处理的场景,如输入框搜索、窗口大小调整等。
  • 节流(Throttle) :节流的原理是在规定的时间间隔内,无论事件触发了多少次,都只执行一次函数。也就是说,它会确保函数以一定的频率执行,而不是无限制地响应事件触发。节流常用于需要频繁但有规律地执行某些操作的场景,如滚动监听、鼠标移动等。

三、实现防抖

现在我们有这样一个任务,做一个类似百度的搜索框,只是简单实现一下它的推荐功能。

防抖节流,必须必拿下!

当我们想要搜索到需要的内容,一边输字,下面就会一边跳出包含输入内容的可能需要内容。这真的是太香了,用过了都说好。毕竟有时候我们不是很记得想要搜索的内容到底是什么,只能依稀记住一些关键字,或是我们要搜索的内容比较长,当刚输入第一个关键字时,下面就惊讶地跳出了那么一大串想要的内容。 但是,为了缓解服务器压力,并不能过于频繁地跳出下面的内容...

1、创建后端数据

{
  "users": [
    {
      "id": "1",
      "name": "张三"
    },
    {
      "id": "2",
      "name": "李四"
    }
  ],
  "posts": [
    {
      "id": "7386299931399209010",
      "title": "给我一个promise,就跟着你了",
      "userID": "1"
    },
    {
      "id": "7385776238788624393",
      "title": "时间管理大师:V8说异步就得排异步的队",
      "userID": "2"
    },
    {
      "id": "7385132376584945701",
      "title": "尝尝这块”曲奇“[cookie]",
      "userID": "3"
    }
  ]
}
  "scripts": {
    "dev": "json-server --watch db.json"
  }
  
  "dependencies": {
    "json-server": "^1.0.0-beta.1"
  }

下载"json-server"依赖来模拟后端接口,快速创建一个模拟的RESTful API服务器。配置文件中加入 "json-server --watch db.json",快速启动服务器。

2、搭建页面

<div>
    没有防抖的input 
    <input 
        type="text" 
        id="unDebounce" 
        placeholder="请输入您要搜索的用户名"
    >
</div>
<div>
    防抖后的input 
    <input 
        type="text" 
        id="debounce" 
        placeholder="请输入您要搜索的用户名"
    >
</div>

防抖节流,必须必拿下!

简单搭建两个输入框,分别用来表示使用防抖前后使用后的输入框使用起来的区别。因为我个人看不惯原生的页面,也就加了些样式,稍微看得顺眼。

3、逻辑实现

现在开始给两个输入框加上键盘监听,并给第二个输入框加上防抖功能,看看最后两者的区别。

1、获取Dom节点

const inputa = document.getElementById('unDebounce'); 
const inputb = document.getElementById('debounce');

获取了两个输入框的引用,getElementById 方法返回页面中 ID 指定元素的引用。

2、定义处理函数

function handleNameSearch(e) {
    const value = e.target.value;
    fetch('http://localhost:3000/users')
        .then(res => res.json())
        .then(data => {
            const users = data;
            const filteredUsers = users.filter(user => {
                return user.name.includes(value)
            });
            console.log(filteredUsers);
        });
}
  1. 上次我们讲过了promise,就会发现其实fetch也应该是被promise包裹了,不然无法使用.then的。
  2. 当输入框内容改变时会调用 fetch 请求数据,并使用 filter 方法过滤出包含当前输入值的用户列表。
  3. 这里除了使用includes方法,也可以用indexOf(),但是因为用不上下标,所以从性能考虑建议还是使用includes

3、定义防抖函数

function debounce(func, delay) {
    let timeoutId;
    return function(...args) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => {
            func.apply(this, args);
        }, delay);
    };
}
  1. debounce 函数接受一个函数 func 和延迟时间 delay。它返回一个新的函数,这个新函数会在最后一次调用后等待 delay 时间,然后执行原始函数。如果在这 delay 时间内被再次调用,则会重置计时器。
  2. 这里设计一个timeoutId作为定时器的id,以便下次准确地清除之前的定时器,来重置时间。
  3. 为了模块化和代码复用,所以利用闭包将防抖函数和处理函数分开来。这意味着如果你有多个不同的处理函数,只要它们都需要防抖,都可以使用同一个debounce 函数来包装,而无需为每个处理函数单独实现防抖逻辑。

4、绑定事件监听器i

inputa.addEventListener('keyup', handleNameSearch);fangdao
const debounceNameSearch = debounce(handleNameSearch, 500);
inputb.addEventListener('keyup', debounceNameSearch);

为第一个输入框简单绑定了 handleNameSearch 函数。对于第二个输入框,首先使用 debounce 函数创建了一个防抖版本的 handleNameSearch 函数,然后将其绑定到 keyup 事件上。 最后的效果就不在这展示了,可以拿着代码自己动手试试。(●'◡'●)

四、实现节流

其实在学会理解了什么是防抖后,可以自己认真思考,一个是在最后一次输入后,展示结果;另一个是按照一定的频率,在一定时间内一定要且只有展示一次。 基本和防抖一样,但会更加细致,一起来看看吧。

逻辑实现

那些搭建页面什么的,大差不差,也就不在这里继续堆代码了。并且简单的获取节点,绑定事件按也不重复,照着前面自己写一些,这里直接开始JS中的函数实现吧。

1、定义处理函数

const ajax = (content) => {
    console.log(`ajax request ${content}`)
}

定义一个模拟耗时操作的函数 ajax,在这里只是一个简单的控制台输出,但在真实场景中这可能是发起一个AJAX请求。

2、定义节流函数

const throttle = (func, delay) => {
    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)
        }
    }
}

节流函数 throttle 接受一个函数 func 和一个延迟时间 delay。内部通过 last 记录上一次执行的时间点,和 deferTimer 定时器ID来管理定时任务。

当输入框事件触发时:

  1. 计算当前时间 now
  2. 如果距离上次执行时间小于 delay,则清除之前的定时器并重新设置定时器。
  3. 如果距离上次执行时间大于等于 delay,则直接执行函数 func 并更新 last 时间。

看起来如果if里面什么都不写,好像也可以实现每隔一定时间触发一次。但是,必须考虑到如果用户在delay时间内没有按下按键呢。

clearTimeout(deferTimer) 
deferTimer = setTimeout(() => { 
    last = now 
    func(args) 
}, delay)

所以当用户不触发耗时事件(keyup)了,这样做可以让最后一次耗时事件再执行一次呢。只能说保证了用户的使用体验。

五、结语

今天从图片懒加载引出了解决事件触发频率的思想,防抖和节流。了解了它们的基本定义,以及不同的适用场景。接着又一起从实际出发,一步步完成了防抖和节流的功能。 看完希望对你有一点帮助,一起加油!✌️

转载自:https://juejin.cn/post/7386969940941586466
评论
请登录