前端优化 —— 函数的节流和防抖

墨子
• 阅读 3715

前端优化 —— 函数的节流和防抖

阅读原文


前言

在前端开发当中我们经常会绑定一些事件触发的某些程序执行,有时这些事件会连续触发,如浏览器窗口 scrollresize,输入框的 keyupinput,以及 click 事件在连续点击时连续发送请求等等,这些情况有些会严重影响前端性能,有些会增加服务器压力,使用户体验大打折扣,而函数节流和防抖就是为了解决这样的问题。

函数节流 throtter

函数节流:当持续发生事件时,保证在一个固定的时间间隔只执行一次真正的事件处理程序,通俗的说就像 “节流” 的名字一样,打开水龙头时要秉承勤俭节约的原则,把阀门关小,最好是达到在固定间隔内水一滴一滴的往下流。

1、节流函数的时序图

前端优化 —— 函数的节流和防抖

从图中可以看出,连续触发事件时,真正执行事件处理程序的间隔是固定的,多次触发,也只会在某一个时间间隔内触发一次,由于事件处理函数内部执行逻辑各不相同,我们就封装一版可通用的节流函数。

2、节流函数的封装

// 文件:throtter.js
// 节流函数
const throtter = (func, delay = 60) => {
    // 锁的标识
    let lock = false;

    // 返回一个事件处理函数
    return (...args) => {
        // 如果 lock 为 true 则跳出
        if (lock) return;

        // 执行函数并更改锁的状态
        func(...args);
        lock = true;

        // 添加定时器,在到达时间间隔时重置锁的状态
        setTimeout(() => lock = false, delay);
    }
}

throtter 函数有两个参数,第一个参数为在事件触发时真正要执行的函数,第二个参数为定义的间隔时间,在函数执行时定义了 lock 的初始值,通过闭包返回一个函数作为事件处理函数,在返回的函数内部判断 lock 状态并确定执行真正的函数 func 还是跳出,每次执行 func 后会更改 lock 状态,通过定时器在规定的时间间隔内重置 lock,这就是函数节流的原理。

3、验证节流函数

// 文件:throtter-test.js
// 使用节流函数
document.addEventListener("scroll", throtter(console.log));

上面我们给 document 对象添加了滚动事件,并不断的打印事件对象,事件处理函数的默认参数为事件对象,从执行效果应该可以看出,平均每 60ms 才会触发一次事件,达到了优化性能的目的,如果想让真正执行的函数 func 传入更多的参数,只需如下处理。

// 文件:throtter-test.js
// a b 为函数要传入的参数
let a = 1, b = 2;

// 返回事件处理函数
const func = throtter(console.log);

// 添加事件监听
document.addEventListener("scroll", e => func(e, a, b));

节流函数一般用于 scrollresize 事件的情况较多,因为这些事件的触发是连续性的,需要在一个时间间隔内只触发一次。

函数防抖 debounce

函数防抖:当持续发生事件时,事件只在上一次触发后的一段时间内没再触发事件,才会真正的执行事件处理逻辑,如果每两次触发的间隔小于这个时间,则不执行事件逻辑。

1、防抖函数的时序图

前端优化 —— 函数的节流和防抖

从图中可以看出,连续触发事件时并没有执行事件处理函数,只有在某一阶段连续触发后的最后一次才执行,也就是上一次触发的时间间隔要大于设定值才执行,同样的,事件处理函数内部执行逻辑各不相同,我们就封装一版可通用的防抖函数。

2、防抖函数的封装

// 文件:debounce.js
// 防抖函数
const dobounce = (func, delay = 300, timer = null) => {
    return (...args) => {
        // 清除定时器
        clearInterval(timer);

        // 在定时器到时后执行事件处理函数
        timer = setTimeout(() => func(...args), delay);
    }
}

dobounce 函数有三个参数,第一个参数为在事件触发时真正要执行的函数,第二个参数为执行事件的延迟时间,第三个参数为定时器 ID 的初始值,执行 dobounce 通过闭包返回了事件处理函数,在处理函数内部先清除定时器,然后定义定时器并将 ID 赋值给 timer,如果事件连续触发,则会不断的清除定时器,直到有一次触发间隔超过了设定延时时间 delay,才会真正执行 func

3、验证防抖函数

<!-- 文件:index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>函数防抖</title>
</head>
<body>
    <input type="text" id="ipt">
</body>
</html>
// 文件:debounce-test.js
// 使用防抖函数
let ipt = document.querySelector("#ipt");

// 添加事件监听
ipt.addEventListener("keyup", debounce(console.log));

上面的功能跟 throtter 类似,真正执行事件处理函数时打印事件对象,通过验证,连续输入触发 keyup 事件,上一次触发和下一次触发间隔时间必须大于 300ms 才会执行打印事件对象的逻辑,如果想传入多个参数套路相同。

// 文件:debounce-test.js
// 获取 dom 元素
let ipt = document.querySelector("#ipt");

// a b 为函数要传入的参数
let a = 1, b = 2;

// 返回事件处理函数
const func = debounce(console.log);

// 添加事件监听
ipt.addEventListener("keyup", e => func(e, a, b));

防抖函数一般用于输入框事件,常用场景就是搜索或查询,如果不使用防抖会连续发送请求,增加服务器的压力,使用防抖后,会在用户输入要查询的关键词后才发送请求,这也更符合用户的习惯,例如百度搜索,就是这样实现的。

总结

“节流” 和 “防抖” 是前端在项目中经常使用的优化手段,代码虽然不多,但是确是前端面试 “出镜率” 非常高的知识点,从而能看出它们的重要性,所以建议前端同学们一定要知道,并能手写,这是 “一箭双雕” 的事,可以用来通过面试,也可以因为工作中遇到直接就写而提高工作效率。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
落落落洛克 落落落洛克
4年前
性能优化之防抖和节流
我的前端学习笔记📒最近花了点时间把笔记整理到语雀上了,方便童鞋们阅读我的前端学习笔记📒(https://www.yuque.com/wanggangfeng
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
1年前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
梦
4年前
微信小程序new Date()转换时间异常问题
微信小程序苹果手机页面上显示时间异常,安卓机正常问题image(https://imghelloworld.osscnbeijing.aliyuncs.com/imgs/b691e1230e2f15efbd81fe11ef734d4f.png)错误代码vardate'2021030617:00:00'vardateT
Stella981 Stella981
3年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Wesley13 Wesley13
3年前
mysql查询每个学生的各科成绩,以及总分和平均分
今天看一个mysql教程,看到一个例子,感觉里面的解决方案不是很合理。问题如下:有学生表:!在这里插入图片描述(https://oscimg.oschina.net/oscnet/07b001b0c6cb7e0038a9299e768fc00a0d3.png)成绩表:!在这里插入图片描述(https://oscimg.o
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
墨子
墨子
Lv1
不管过多少年,我都拒绝不了和你相似的眉眼.
文章
3
粉丝
0
获赞
0