防抖

 

<!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

相關文章
相關標籤/搜索