写在前面
最近在查漏补缺(其实就是菜),发现一段时间不学习后就会被新的知识所淘汰呢,只有不断地努力学习才能不断地提高自己。
今天我们就来讲一下最近前端的几个新名词:防抖、节流、重绘和回流。虽说是新名词,但并不是新技术。
防抖和节流
概念(定义)
防抖:任务频繁触发的情况下,只有任务触发的间隔超过制定的时间间隔的时候,任务才会被执行。
节流:指定时间间隔内只会执行一次任务。
解释
防抖
这里引用了知乎上的一段示例代码:
1 |
|
我们来理解一下代码:
- 我们点击页面上的按钮,执行
debounce函数,并传入参数sayDebounce - 在函数
debounce中返回了一个匿名函数,并在匿名函数中定义了一个定时器。 - 执行匿名函数时,先清除定时器,然后再创建一个新的定时器。
- 延时1秒执行传入的函数
sayDebounce()
程序大致的执行过程已经介绍完了,整个防抖程序的关键就在于第3步中。
为什么这么说呢?
因为,我们每次点击都会去执行匿名函数,匿名函数每次执行前都会去清除一次定时器,然后再重新定义定时器。这样做就会让函数永远都只会在满足定时器制定的时间间隔后才会执行。(可能这样说有点不恰当)。我们改造一下这个sayDebounce函数:
1 | function sayDebounce() { |
这样,我们再次执行程序的时候控制台打印出来的时间永远都是我们最后一次点击的时间加上1秒(不考虑程序执行时间)。
举个栗子:
假设,我今天要去抢手机,手机早上11点整开售,我们10点59分50秒进去疯狂点击,但是由于抢购按钮采用了防抖,且时间间隔为1秒,那么无论我们手速多块,一秒点多少次,都是无效的。因为程序根本不会执行到抢购程序那里,所以最好的抢购时机就是10点59分59秒的时候点击,那么11点整的时候正好执行了抢购程序(不考虑程序执行时间)。
节流
同样,这里也是引用了知乎的一个示例代码:
1 |
|
我们首先来解释一下代码:
- 我们点击页面上的按钮,执行
throttle函数,并传入参数sayThrottle - 在函数
throttle中定义了一个bool变量canRun,赋值为true;还返回了一个匿名函数。 - 在执行匿名函数时,先判断
canRun,若值为false,则return,反之赋值canRun赋值为false,然后再创建一个定时器。 - 在定时器中延时1秒执行传入的函数
sayThrottle(),并将canRun的值赋为true。
程序的大致执行过程已经介绍完了,整个节流程序中核心步骤在第3步与第4步中。
每次我们点击按钮,程序都会去执行匿名函数。在执行匿名函数中的定时器之前,程序会先去判断canRun的值,若值为false,则直接退出程序。反之才会执行定时器。即,程序第一次执行完之前,该程序是无法继续去执行第二次的。我们改造一下这个sayThrottle函数:
1 | function sayThrottle() { |
这样,我们再次执行程序的时候在控制台会依次打印出前一次成功执行后第一个点击的时间(有点绕)。比如,我2019-06-14 10:00:00第一次点击,然后我疯狂点击,比如在10:00:00到10:00:01这一秒内我点了100次,但是控制台最终只会打印出:
1 | 2019-06-14 10:00:00 |
举个栗子:
跟前面一样。假设,我今天要去抢手机,手机早上11点整开售,我们10点59分50秒进去疯狂点击,但是由于抢购按钮采用了节流,且时间间隔为1秒,那么无论我们手速多块,一秒点多少次,都是无效的。因为程序只会在50秒的时候出发,51秒执行完成,完成后因为我们点的速度很快,程序立马又继续被触发。所以无论我们50-51秒内点击了多少次都不会影响程序的正常执行的(不考虑程序执行时间)。
区别
函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。 比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。
总结
- 函数防抖:将多次操作合并为一次操作进行。原理是维护一个计时器,规定在一定时间后触发函数,但是在规定时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。
- 函数节流:使得一定时间内只触发一次函数。原理是通过判断是否有延迟调用函数未执行。
- 函数防抖属于阻止函数继续执行,函数节流属于稀释函数执行频率。
重绘和回流
概念(定义)
重绘(repaint):当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时由于只需要 UI 层面的重新像素绘制。
回流(reflow):又叫重排(layout),当元素的尺寸、结构或者触发某些属性时,浏览器会重新渲染页面,称为回流。此时,浏览器需要重新经过计算,计算后还需要重新页面布局。
常见操作
- 重绘:
- 改变元素颜色
- 改变元素背景色
- ……
- 回流:
- 页面首次渲染时
- 浏览器窗口大小改变
- 元素尺寸、位置和内容发生改变
- 字体大小的变化
- 添加或删除DOM元素
- 激活CSS伪类
- ……
注意:回流必定会触发重绘,重绘不一定会触发回流。重绘的开销较小,回流的代价较高。
与防抖和节流的关系
为什么需要 节流?它与回流有什么关系?
因为有些事情会造成浏览器的 回流,而 回流 会使浏览器开销增大,所以我们通过 节流 来防止这种增大浏览器开销的事情。
举个栗子:
页面上有个 div 框,用户可以在 input 框中输入 div 框的一些信息,例如宽、高等,输入完毕立即改变属性。但是,因为改变之后还要随时存储到数据库中,所以需要调用接口。如果不加限制,每输入完一个属性后都会造成一次回流和一次接口的调用,这样会大大增加浏览器的开销。
如何减少使用
既然重绘和回流都会增加浏览器的开销,那么我们因该怎样去避免大量使用他们呢?
- 避免频繁操作样式,可汇总后统一一次修改
- 尽量使用 class 进行样式修改,而不是直接操作样式
- 减少 DOM 的操作,可使用字符串一次性插入
做到这三点,就可以大大减少回流与重绘的使用。