2019 面試準備 - JS 防抖與節流

jsliang發表於2019-03-12

Create by jsliang on 2019-2-23 20:55:34
Recently revised in 2019-3-12 21:34:59

Hello 小夥伴們,如果覺得本文還不錯,記得給個 star , 你們的 star 是我學習的動力!GitHub 地址

本文涉及知識點

  • 防抖與節流
  • 重繪與迴流
  • 瀏覽器解析 URL
  • DNS 域名解析
  • TCP 三次握手與四次揮手
  • 瀏覽器渲染頁面

在本文中,jsliang 會講解通過自我探索後關於上述知識點的個人理解,如有紕漏、疏忽或者誤解,歡迎各位小夥伴留言指出。

如果小夥伴對文章存有疑問,想快速得到回覆。
或者小夥伴對 jsliang 個人的前端文件庫感興趣,也想將自己的前端知識整理出來。
歡迎加 QQ 群一起探討:798961601

一 目錄

不折騰的前端,和鹹魚有什麼區別

目錄
一 目錄
二 前言
三 防抖與節流
3.1 防抖
3.2 節流
四 重繪與迴流
五 瀏覽器解析 URL
六 DNS 域名解析
七 TCP 三次握手與四次揮手
八 瀏覽器渲染頁面
九 總結
十 參考文獻

二 前言

返回目錄

在工作中,我們可能碰到這樣的問題:

  • 使用者在搜尋的時候,在不停敲字,如果每敲一個字我們就要調一次介面,介面呼叫太頻繁,給卡住了。
  • 使用者在閱讀文章的時候,我們需要監聽使用者滾動到了哪個標題,但是每滾動一下就監聽,那樣會太過頻繁從而佔記憶體,如果再加上其他的業務程式碼,就卡住了。

所以,這時候,我們就要用到 防抖與節流 了。

那麼,講到 防抖與節流,我們可以順帶探祕下 重繪與迴流

說起 重繪與迴流,我們就順帶把 瀏覽器輸入 URL 後發生的事情 也關注一下,從而引出 DNSTCP 等知識點,最終串起來構成本文的輪廓,方便 jsliang 和小夥伴們對這塊知識的整理與記憶。

三 防抖與節流

返回目錄

通過程式碼去了解某樣事物,往往是瞭解某個知識點最快的形式。

3.1 防抖

返回目錄

下面我們有段防抖小案例程式碼。

如果小夥伴們手頭有電腦,並感興趣想先自己思考下什麼是防抖。可以將程式碼複製到瀏覽器,嘗試點選按鈕,並關注下控制檯,看看 Console 是如何列印的。

如果小夥伴們手頭沒有電腦,那麼我們一起先瞅瞅程式碼實現,再看看下面 GIF 演示。(這樣效果沒有自己敲的直白有效)

<!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() {
      // 1、獲取這個按鈕,並繫結事件
      var myDebounce = document.getElementById("debounce");
      myDebounce.addEventListener("click", debounce(sayDebounce));
    }

    // 2、防抖功能函式,接受傳參
    function debounce(fn) {
      // 4、建立一個標記用來存放定時器的返回值
      let timeout = null;
      return function() {
        // 5、每次當使用者點選/輸入的時候,把前一個定時器清除
        clearTimeout(timeout);
        // 6、然後建立一個新的 setTimeout,
        // 這樣就能保證點選按鈕後的 interval 間隔內
        // 如果使用者還點選了的話,就不會執行 fn 函式
        timeout = setTimeout(() => {
          fn.call(this, arguments);
        }, 1000);
      };
    }

    // 3、需要進行防抖的事件處理
    function sayDebounce() {
      // ... 有些需要防抖的工作,在這裡執行
      console.log("防抖成功!");
    }

  </script>
</body>
</html>
複製程式碼

很好,相信小夥伴們已經看完了程式碼,下面我們看看它的演示:

2019 面試準備 - JS 防抖與節流

這時候,我們可以丟擲防抖的概念了:

  • 防抖任務頻繁觸發的情況下,只有任務觸發的間隔超過指定間隔的時候,任務才會執行。

結合上面的程式碼,我們可以瞭解到,在觸發點選事件後,如果使用者再次點選了,我們會清空之前的定時器,重新生成一個定時器。意思就是:這件事兒需要等待,如果你反覆催促,我就重新計時!

空講無益,show you 場景:

  • 有個輸入框,輸入之後會呼叫介面,獲取聯想詞。但是,因為頻繁呼叫介面不太好,所以我們在程式碼中使用防抖功能,只有在使用者輸入完畢的一段時間後,才會呼叫介面,出現聯想詞。

小夥伴們可以嘗試看著上面的案例,先自己實現一遍這個場景的解決,如果感覺不行,那就看:《防抖和節流的應用場景和實現》

知識點補充:何為 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() {
      // 1、獲取按鈕,繫結點選事件
      var myThrottle = document.getElementById("throttle");
      myThrottle.addEventListener("click", throttle(sayThrottle));
    }

    // 2、節流函式體
    function throttle(fn) {
      // 4、通過閉包儲存一個標記
      let canRun = true;
      return function() {
        // 5、在函式開頭判斷標誌是否為 true,不為 true 則中斷函式
        if(!canRun) {
          return;
        }
        // 6、將 canRun 設定為 false,防止執行之前再被執行
        canRun = false;
        // 7、定時器
        setTimeout( () => {
          fn.call(this, arguments);
          // 8、執行完事件(比如呼叫完介面)之後,重新將這個標誌設定為 true
          canRun = true;
        }, 1000);
      };
    }

    // 3、需要節流的事件
    function sayThrottle() {
      console.log("節流成功!");
    }

  </script>
</body>
</html>
複製程式碼

很好,看完程式碼的小夥伴應該大致清楚是怎麼回事了,下面我們看 GIF 實現:

2019 面試準備 - JS 防抖與節流

看完程式碼和 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 框的一些資訊,例如寬、高等,輸入完畢立即改變屬性。但是,因為改變之後還要隨時儲存到資料庫中,所以需要呼叫介面。如果不加限制……

看到這裡,小夥伴們可以將一些字眼結合起來了:為什麼需要 節流,因為有些事情會造成瀏覽器的 迴流,而 迴流 會使瀏覽器開銷增大,所以我們通過 節流 來防止這種增大瀏覽器開銷的事情。

形象地用圖來說明:

2019 面試準備 - JS 防抖與節流

這樣,我們就可以形象的將 防抖與節流重繪與迴流 結合起來記憶起來。

那麼,在工作中我們要如何避免大量使用重繪與迴流呢?:

  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 這樣寫會被懟我就清楚,所以,我們還是結合上面的圖,進一步勾勒我們的結構:

2019 面試準備 - JS 防抖與節流

很好,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 畫張圖過一遍,就感覺清晰了:

2019 面試準備 - JS 防抖與節流

七 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:

2019 面試準備 - JS 防抖與節流

  • 四次揮手
  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:

2019 面試準備 - JS 防抖與節流

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:《TCP三次握手和四次揮手過程》
參考文獻 2:《TCP的三次握手與四次揮手(詳解+動圖)》

八 瀏覽器渲染頁面

返回目錄

最後,我們解決第三個問題:

  • 瀏覽器解析文件資源並渲染頁面是個怎樣的流程?

話不多說,一起來看:

  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 畫了幾張圖也累了,所以我們們 來了一張圖:

2019 面試準備 - JS 防抖與節流

這樣,我們就對 瀏覽器渲染頁面過程 一清二楚啦~

參考文獻:《一篇文章搞定前端面試》

九 總結

返回目錄

至此,我們回顧下自己做了什麼?

  1. 我們在工作中碰到一些問題,這些問題會卡住頁面,於是我們查資料,知道想要減少瀏覽器的開銷,我們就需要使用 防抖與節流
  2. 使用 防抖與節流 解決完問題後,我們好奇為什麼會有這樣的操作,於是我們深入瞭解了下 重繪與迴流
  3. 重繪與迴流 只告訴了我們瀏覽器在 CSS 上的渲染,我們需要進一步瞭解 瀏覽器渲染頁面 的詳細過程,但洋蔥還是要一層一層剝開的,所以我們需要從 瀏覽器解析 URL 開始瞭解。
  4. 瀏覽器解析 URL 中,我們順帶了解下 DNS 域名解析TCP 三次握手與四次揮手 這兩個知識點。
  5. 最後,我們終於知道了 瀏覽器渲染頁面 是怎麼一回事。

綜上,如果我們僅僅是需要關注面試的一個點,我們很可能因為不知頭尾,而被面試官問得啞口無言。

但是,如果我們知道一個知識點,並對其進行思路發散,深入學習,相信面試官問起來的時候,小夥伴們就可以侃侃而談,而不會被問地體無完膚了!

最後祝小夥伴們找到合適的滿意的工作~

十 參考文獻

返回目錄

  1. 《函式防抖和節流》
  2. 《節流 & 防抖》
  3. 《JS奇淫巧技:防抖函式與節流函式》
  4. 《閒聊 JS 中的 apply 和 call》
  5. 《js 中 arguments 的用法》
  6. 《防抖和節流的應用場景和實現》
  7. 《網頁解析的全過程(輸入url到展示頁面)》
  8. 《瀏覽器渲染頁面過程剖析》
  9. 《一篇文章搞定前端面試》

jsliang 廣告推送:
也許小夥伴想了解下雲伺服器
或者小夥伴想買一臺雲伺服器
或者小夥伴需要續費雲伺服器
歡迎點選 雲伺服器推廣 檢視!

2019 面試準備 - JS 防抖與節流
2019 面試準備 - JS 防抖與節流

知識共享許可協議
jsliang 的文件庫樑峻榮 採用 知識共享 署名-非商業性使用-相同方式共享 4.0 國際 許可協議進行許可。
基於github.com/LiangJunron…上的作品創作。
本許可協議授權之外的使用許可權可以從 creativecommons.org/licenses/by… 處獲得。

相關文章