防抖

 

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>防抖</title> </head> <body> <button id="debounce">点我防抖!</button> <script> window.onload = function() { // 一、获取这个按钮,并绑定事件 var myDebounce = document.getElementById("debounce"); myDebounce.addEventListener("click", debounce(sayDebounce)); } // 二、防抖功能函数,接受传参 function debounce(fn) { // 四、建立一个标记用来存放定时器的返回值 let timeout = null; return function() { // 五、每次当用户点击/输入的时候,把前一个定时器清除 clearTimeout(timeout); // 六、而后建立一个新的 setTimeout, // 这样就能保证点击按钮后的 interval 间隔内 // 若是用户还点击了的话,就不会执行 fn 函数 timeout = setTimeout(() => { fn.call(this, arguments); }, 1000); }; } // 三、须要进行防抖的事件处理 function sayDebounce() { // ... 有些须要防抖的工做,在这里执行 console.log("防抖成功!"); } </script> </body> </html> 复制代码

很好,相信小伙伴们已经看完了代码,下面咱们看看它的演示:javascript

 

 

这时候,咱们能够抛出防抖的概念了:html

  • 防抖任务频繁触发的状况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行。

结合上面的代码,咱们能够了解到,在触发点击事件后,若是用户再次点击了,咱们会清空以前的定时器,从新生成一个定时器。意思就是:这件事儿须要等待,若是你反复催促,我就从新计时!前端

空讲无益,show you 场景:java

  • 有个输入框,输入以后会调用接口,获取联想词。可是,由于频繁调用接口不太好,因此咱们在代码中使用防抖功能,只有在用户输入完毕的一段时间后,才会调用接口,出现联想词。

小伙伴们能够尝试看着上面的案例,先本身实现一遍这个场景的解决,若是感受不行,那就看:《防抖和节流的应用场景和实现》web

知识点补充:何为 arguments
首先,后端转前端的同窗,能够将 arguments 理解为能实现重载函数功能的工具。
而后,咱们举个例子:在 function test() 这个方法中,因为咱们不肯定变量有多少,好比 test("jsliang", 24),又或者 test("LiangJunrong", "jsliang", "24"),这时候只须要在函数 test 中用 arguments 接收就好了。
最后,在 function test() { let arr1 = argument[0] } 中,arr1 就能够获取到传进来的第一个变量。
因此fn.call(this, arguments) 实际上是将不肯定变量替换到函数中了。面试

参考资料 1:《闲聊 JS 中的 apply 和 call》
参考资料 2:《js 中 arguments 的用法》数据库

3.2 节流

说完防抖,下面咱们讲讲节流,规矩就不说了,先上代码:后端

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>节流</title> </head> <body> <button id="throttle">点我节流!</button> <script> window.onload = function() { // 一、获取按钮,绑定点击事件 var myThrottle = document.getElementById("throttle"); myThrottle.addEventListener("click", throttle(sayThrottle)); } // 二、节流函数体 function throttle(fn) { // 四、经过闭包保存一个标记 let canRun = true; return function() { // 五、在函数开头判断标志是否为 true,不为 true 则中断函数 if(!canRun) { return; } // 六、将 canRun 设置为 false,防止执行以前再被执行 canRun = false; // 七、定时器 setTimeout( () => { fn.call(this, arguments); // 八、执行完事件(好比调用完接口)以后,从新将这个标志设置为 true canRun = true; }, 1000); }; } // 三、须要节流的事件 function sayThrottle() { console.log("节流成功!"); } </script> </body> </html> 复制代码

很好,看完代码的小伙伴应该大体清楚是怎么回事了,下面咱们看 GIF 实现:浏览器

 

 

看完代码和 GIF 实现,咱们能够明白,节流便是:缓存

  • 节流指定时间间隔内只会执行一次任务。

那么,节流在工做中的应用?

  1. 懒加载要监听计算滚动条的位置,使用节流按必定时间的频率获取。
  2. 用户点击提交按钮,假设咱们知道接口大体的返回时间的状况下,咱们使用节流,只容许必定时间内点击一次。

这样,在某些特定的工做场景,咱们就可使用防抖与节流来减小没必要要的损耗。

那么问题来了,假设面试官听到你这句话,是否是会接着问一句:“为何说上面的场景不节制会形成过多损耗呢?”

OK,这就涉及到浏览器渲染页面的机制了……

四 重绘与回流

在说浏览器渲染页面以前,咱们须要先了解两个点,一个叫 浏览器解析 URL,另外一个就是本章节将涉及的 重绘与回流

  • 重绘(repaint):当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时因为只须要 UI 层面的从新像素绘制,所以损耗较少

常见的重绘操做有:

  1. 改变元素颜色
  2. 改变元素背景色
  3. more ……
  • 回流(reflow):又叫重排(layout)。当元素的尺寸、结构或者触发某些属性时,浏览器会从新渲染页面,称为回流。此时,浏览器须要从新通过计算,计算后还须要从新页面布局,所以是较重的操做。

常见的回流操做有:

  1. 页面初次渲染
  2. 浏览器窗口大小改变
  3. 元素尺寸/位置/内容发生改变
  4. 元素字体大小变化
  5. 添加或者删除可见的 DOM 元素
  6. 激活 CSS 伪类(:hover……)
  7. more ……
  • 重点回流一定会触发重绘,重绘不必定会触发回流。重绘的开销较小,回流的代价较高。

看到这里,小伙伴们可能有点懵逼,你刚刚还跟我讲着 防抖与节流 ,怎么一会儿跳到 重绘与回流 了?

OK,卖个关子,先看下面场景:

  • 界面上有个 div 框,用户能够在 input 框中输入 div 框的一些信息,例如宽、高等,输入完毕当即改变属性。可是,由于改变以后还要随时存储到数据库中,因此须要调用接口。若是不加限制……

看到这里,小伙伴们能够将一些字眼结合起来了:为何须要 节流,由于有些事情会形成浏览器的 回流,而 回流 会使浏览器开销增大,因此咱们经过 节流 来防止这种增大浏览器开销的事情。

形象地用图来讲明:

 

 

这样,咱们就能够形象的将 防抖与节流重绘与回流 结合起来记忆起来。

那么,在工做中咱们要如何避免大量使用重绘与回流呢?:

  1. 避免频繁操做样式,可汇总后统一一次修改
  2. 尽可能使用 class 进行样式修改,而不是直接操做样式
  3. 减小 DOM 的操做,可以使用字符串一次性插入

OK,至此咱们就讲完两个部分了,那么问题又来了:“浏览器渲染过程当中,是否是也有重绘与回流?”“从浏览器输入 URL 到渲染成功的过程当中,究竟发生了什么?”

咱们,继续深刻探索……

五 浏览器解析 URL

为了能让咱们的知识层面看起来更有深度,咱们应该考虑下面两个问题了:

  • 从浏览器输入 URL 到渲染成功的过程当中,究竟发生了什么?
  • 浏览器渲染过程当中,发生了什么,是否是也有重绘与回流?

OK,兴致来了,咱们就先从 浏览器解析 URL 看起,先来看看当用户输入 URL,到浏览器呈现给用户页面,经历了如下过程:

  • 版本 A
  1. 用户输入 URL 地址。
  2. 对 URL 地址进行 DNS 域名解析。
  3. 创建 TCP 链接(三次握手)。
  4. 浏览器发起 HTTP 请求报文。
  5. 服务器返回 HTTP 响应报文。
  6. 关闭 TCP 链接(四次挥手)。
  7. 浏览器解析文档资源并渲染页面。

讲到这里,忽然想起一个对话:

学生:“老师,这门课的考试重点是什么?”

老师:“全都是重点!”

enm...老师会不会被打我不知道,可是 jsliang 这样写会被怼我就清楚,因此,咱仍是结合上面的图,进一步勾勒咱们的结构:

 

 

很好,jsliang 感受本身的画图技术又进了一步~

①:虽然很感激网上有那么多的文章能够参考,可是在我查了二十来篇文章后,jsliang 以为这部分十有八九有问题撒,问了些小伙伴,它们有的说对,有的说错。不过,不妨碍小伙伴们继续往下看哈。
②:为了不出篓子,下面贴出另一个版本,小伙伴们能够在评论区说出你支持哪一个版本哈:

  • 版本 B
  1. 用户输入 URL 地址。
  2. 对 URL 地址进行 DNS 域名解析。
  3. 进行 TCP 链接。
  4. 进行 HTTP 报文的请求与响应。
  5. 浏览器解析文档资源并渲染页面。

在这里咱们能够清晰的了解到从 用户输入 URL,到浏览器呈现给用户页面,经历了哪些过程

那么剩下的就简单了:

  1. 什么是 DNS 解析,它是怎么个流程?
  2. 什么是 TCP 三次握手,什么是 TCP 四次挥手,它们的流程是怎样的?
  3. 浏览器解析文档资源并渲染页面是个怎样的流程?

Let's go~ 逐步完成下面三个知识点!

参考文献 1:《网页解析的全过程(输入url到展现页面)》
参考文献 2:《浏览器渲染页面过程剖析》

六 DNS 域名解析

首先,咱们解决第一个问题:

  • 什么是 DNS 解析,它是怎么个流程?

DNS(Domain Name System)是 域名系统 的英文缩写,提供的服务是用于将主机名和域名转换为 IP 地址的工做:

域名:http://jsliang.top <---> DNS <---> IPV4:119.147.15.13

IPV4 是造假的,仅用来讲明 DNS 解析后能返回 IP 地址

因此,当用户在浏览器输入 http://jsliang.top 时,DNS 经历了如下步骤:

  1. 浏览器根据地址,在自身缓存中查找 DNS(域名服务器) 中的解析记录。若是存在,则直接返回 IP 地址;若是不存在,则查找操做系统中的 hosts 文件是否有该域名的 DNS 解析记录,若是有就返回。
  2. 在条件 1 中的浏览器缓存或者操做系统的 hosts 文件中都没有这个域名的 DNS 解析记录,或者已通过期,则向域名服务器发起请求解析这个域名。
  3. 先向本地域名服务器中请求,让它解析这个域名,若是解析不了,则向根域名服务器请求解析。
  4. 根服务器给本地域名服务器返回一个主域名服务器。
  5. 本地域名服务器向主域名服务器发起解析请求。
  6. 主域名服务器接收到解析请求后,查找并返回域名对应的域名服务器的地址。
  7. 域名服务器会查询存储的域名和 IP 的映射关系表,返回目标 IP 记录以及一个 TTL(Time To Live)值。
  8. 本地域名服务器接收到 IP 和 TTL 值,进行缓存,缓存的时间由 TTL 值控制。
  9. 将解析的结果返回给用户,用户根据 TTL 值缓存在本地系统缓存中,域名解析过程结束。

看文字老是难以理解的,跟着 jsliang 画张图过一遍,就感受清晰了:

 

 

七 TCP 三次握手与四次挥手

而后,咱们解决第二个问题:

  • 什么是 TCP 三次握手,什么是 TCP 四次挥手,它们的流程是怎样的?

什么是 TCP 呢?TCP(Transmission Control Protocol 传输控制协议)是一种面向链接的、可靠的、基于字节流的传输层通讯协议。

简单来讲,它的做用就是将数据流从一台主机可靠地传输到另外一台主机。

至于具体的工做原理,这里暂时涉及不到,咱们目前只想知道两个点:三次握手与四次挥手

  • 三次握手
  1. 第一次握手:起初两端都处于 CLOSED 关闭状态,Client 将标志位 SYN 置为 1,随机产生一个值 seq = x,并将该数据包发送给 Server,Client 进入 SYN-SENT 状态,等待 Server 确认。
  2. 第二次握手:Server 收到数据包后由标志位 SYN = 1 得知 Client 请求创建链接,Server 将标志位 SYN 和 ACK 都置为 1,ack = x + 1,随机产生一个值 seq = y,并将该数据包发送给Client以确认链接请求,Server 进入 SYN-RCVD 状态,此时操做系统为该 TCP 链接分配 TCP 缓存和变量。
  3. 第三次握手:Client 收到确认后,检查 seq 是否为 x + 1,ACK 是否为 1,若是正确则将标志位 ACK 置为 1,ack = y + 1,而且此时操做系统为该 TCP 链接分配 TCP 缓存和变量,并将该数据包发送给 Server,Server 检查 ack 是否为 y + 1,ACK 是否为 1,若是正确则链接创建成功,Client 和 Server 进入 established 状态,完成三次握手,随后 Client 和 Server 就能够开始传输数据。

文字太乱,show you picture:

 

 

  • 四次挥手
  1. 第一次挥手:Client 的应用进程先向其 TCP 发出链接释放报文段(FIN = 1,序号 seq = u),并中止再发送数据,主动关闭 TCP 链接,进入 FIN-WAIT-1(终止等待1)状态,等待 Server 的确认。
  2. 第二次挥手:Server 收到链接释放报文段后即发出确认报文段,(ACK = 1,确认号 ack = u + 1,序号 seq = v),Server 进入 CLOSE-WAIT(关闭等待)状态,此时的 TCP 处于半关闭状态,Client 到 Server 的链接释放。

注:Client 收到 Server 的确认后,进入 FIN-WAIT-2(终止等待2)状态,等待 Server 发出的链接释放报文段。

  1. 第三次挥手:Server 已经没有要向 Client 发出的数据了,Server 发出链接释放报文段(FIN = 1ACK = 1,序号 seq = w,确认号 ack = u + 1),Server 进入 LAST-ACK(最后确认)状态,等待 Client 的确认。
  2. 第四次挥手:Client 收到 Server 的链接释放报文段后,对此发出确认报文段(ACK = 1seq = u + 1ack = w + 1),Client 进入 TIME-WAIT(时间等待)状态。此时 TCP 未释放掉,须要通过时间等待计时器设置的时间 2MSL 后,Client 才进入 CLOSED 状态。

文字太乱,show you picture:

 

 

OK,至此咱们就理解了 TCP 及其三次握手和四次挥手过程,为了方便小伙伴们形象记忆,jsliang 搞了个小故事,但愿小伙伴们能加深印象:

  • 三次握手 + 四次挥手形象记忆
  1. jsliang:(对妹子发起微信好友申请)“你好,我能够加你好友吗?” —— 第一次握手
  2. 妹子:(经过审核)“你好,很高兴认识你~” —— 第二次握手
  3. jsliang:“你好,我叫梁峻荣,前端折腾小能手……” —— 第三次握手
  4. ……(聊天内容)
  5. …………(聊天内容)
  6. ………………(聊天内容)
  7. …………(聊天内容)
  8. ……(聊天内容)
  9. jsliang:(感冒拍了张纸篓都是纸巾的图)“啊,好难受今天。” —— 第一次挥手
  10. 妹子:“卧槽,你好恶心!” —— 第二次挥手
  11. 妹子:“咱仍是当不认识吧,互删了,谢谢!” —— 第三次挥手
  12. jsliang:(呆)“不是,你听我说!” —— 第四次挥手
  13. 妹子:(果断删除好友) —— CLOSED
  14. jsliang:(!“我今天感冒了。” 妹子开启了好友验证,你还不是她好友。请先发送好友验证请求,对方验证经过后,才能聊天。) ——— CLOSED

OK,成功出糗,相信小伙伴们有了个很好的了解了。

那么,咱们继续前行探索。

八 浏览器渲染页面

最后,咱们解决第三个问题:

  • 浏览器解析文档资源并渲染页面是个怎样的流程?

话很少说,一块儿来看:

  1. 浏览器经过 HTMLParser 根据深度遍历的原则把 HTML 解析成 DOM Tree。
  2. 浏览器经过 CSSParser 将 CSS 解析成 CSS Rule Tree(CSSOM Tree)。
  3. 浏览器将 JavaScript 经过 DOM API 或者 CSSOM API 将 JS 代码解析并应用到布局中,按要求呈现响应的结果。
  4. 根据 DOM 树和 CSSOM 树来构造 render Tree。
  5. layout:重排(也能够叫回流),当 render tree 中任一节点的几何尺寸发生改变,render tree 就会从新布局,从新来计算全部节点在屏幕的位置。
  6. repaint:重绘,当 render tree 中任一元素样式属性(几何尺寸没改变)发生改变时,render tree 都会从新画,好比字体颜色,背景等变化。
  7. paint:遍历 render tree,并调动硬件图形 API 来绘制每一个节点。

文字讲解确定仍是不够清晰的,可是 jsliang 画了几张图也累了,因此我们 来了一张图:

 

 

这样,咱们就对 浏览器渲染页面过程 一清二楚

 

至此,咱们回顾下本身作了什么?

  1. 咱们在工做中碰到一些问题,这些问题会卡住页面,因而咱们查资料,知道想要减小浏览器的开销,咱们就须要使用 防抖与节流
  2. 使用 防抖与节流 解决完问题后,咱们好奇为何会有这样的操做,因而咱们深刻了解了下 重绘与回流
  3. 重绘与回流 只告诉了咱们浏览器在 CSS 上的渲染,咱们须要进一步了解 浏览器渲染页面 的详细过程,但洋葱仍是要一层一层剥开的,因此咱们须要从 浏览器解析 URL 开始了解。
  4. 浏览器解析 URL 中,咱们顺带了解下 DNS 域名解析TCP 三次握手与四次挥手 这两个知识点。
  5. 最后,咱们终于知道了 浏览器渲染页面 是怎么一回事。

                                                          -----------------小y

相关文章
相关标签/搜索