<!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 的用法》數據庫
說完防抖,下面咱們講講節流,規矩就不說了,先上代碼:後端
<!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 實現,咱們能夠明白,節流便是:緩存
那麼,節流在工做中的應用?
這樣,在某些特定的工做場景,咱們就可使用防抖與節流來減小沒必要要的損耗。
那麼問題來了,假設面試官聽到你這句話,是否是會接着問一句:「爲何說上面的場景不節制會形成過多損耗呢?」
OK,這就涉及到瀏覽器渲染頁面的機制了……
在說瀏覽器渲染頁面以前,咱們須要先了解兩個點,一個叫 瀏覽器解析 URL,另外一個就是本章節將涉及的 重繪與迴流:
常見的重繪操做有:
常見的迴流操做有:
看到這裏,小夥伴們可能有點懵逼,你剛剛還跟我講着 防抖與節流 ,怎麼一會兒跳到 重繪與迴流 了?
OK,賣個關子,先看下面場景:
看到這裏,小夥伴們能夠將一些字眼結合起來了:爲何須要 節流,由於有些事情會形成瀏覽器的 迴流,而 迴流 會使瀏覽器開銷增大,因此咱們經過 節流 來防止這種增大瀏覽器開銷的事情。
形象地用圖來講明:
這樣,咱們就能夠形象的將 防抖與節流 與 重繪與迴流 結合起來記憶起來。
那麼,在工做中咱們要如何避免大量使用重繪與迴流呢?:
OK,至此咱們就講完兩個部分了,那麼問題又來了:「瀏覽器渲染過程當中,是否是也有重繪與迴流?」「從瀏覽器輸入 URL 到渲染成功的過程當中,究竟發生了什麼?」
咱們,繼續深刻探索……
爲了能讓咱們的知識層面看起來更有深度,咱們應該考慮下面兩個問題了:
OK,興致來了,咱們就先從 瀏覽器解析 URL 看起,先來看看當用戶輸入 URL,到瀏覽器呈現給用戶頁面,經歷瞭如下過程:
講到這裏,忽然想起一個對話:
學生:「老師,這門課的考試重點是什麼?」
老師:「全都是重點!」
enm...老師會不會被打我不知道,可是 jsliang 這樣寫會被懟我就清楚,因此,咱仍是結合上面的圖,進一步勾勒咱們的結構:
很好,jsliang 感受本身的畫圖技術又進了一步~
①:雖然很感激網上有那麼多的文章能夠參考,可是在我查了二十來篇文章後,jsliang 以爲這部分十有八九有問題撒,問了些小夥伴,它們有的說對,有的說錯。不過,不妨礙小夥伴們繼續往下看哈。
②:爲了不出簍子,下面貼出另一個版本,小夥伴們能夠在評論區說出你支持哪一個版本哈:
- 版本 B
- 用戶輸入 URL 地址。
- 對 URL 地址進行 DNS 域名解析。
- 進行 TCP 鏈接。
- 進行 HTTP 報文的請求與響應。
- 瀏覽器解析文檔資源並渲染頁面。
在這裏咱們能夠清晰的瞭解到從 用戶輸入 URL,到瀏覽器呈現給用戶頁面,經歷了哪些過程。
那麼剩下的就簡單了:
Let's go~ 逐步完成下面三個知識點!
參考文獻 1:《網頁解析的全過程(輸入url到展現頁面)》
參考文獻 2:《瀏覽器渲染頁面過程剖析》
首先,咱們解決第一個問題:
DNS(Domain Name System)是 域名系統 的英文縮寫,提供的服務是用於將主機名和域名轉換爲 IP 地址的工做:
域名:http://jsliang.top
<---> DNS <---> IPV4:119.147.15.13
IPV4 是造假的,僅用來講明 DNS 解析後能返回 IP 地址
因此,當用戶在瀏覽器輸入 http://jsliang.top
時,DNS 經歷瞭如下步驟:
看文字老是難以理解的,跟着 jsliang 畫張圖過一遍,就感受清晰了:
而後,咱們解決第二個問題:
什麼是 TCP 呢?TCP(Transmission Control Protocol 傳輸控制協議)是一種面向鏈接的、可靠的、基於字節流的傳輸層通訊協議。
簡單來講,它的做用就是將數據流從一臺主機可靠地傳輸到另外一臺主機。
至於具體的工做原理,這裏暫時涉及不到,咱們目前只想知道兩個點:三次握手與四次揮手。
seq = x
,並將該數據包發送給 Server,Client 進入 SYN-SENT 狀態,等待 Server 確認。SYN = 1
得知 Client 請求創建鏈接,Server 將標誌位 SYN 和 ACK 都置爲 1,ack = x + 1
,隨機產生一個值 seq = y
,並將該數據包發送給Client以確認鏈接請求,Server 進入 SYN-RCVD
狀態,此時操做系統爲該 TCP 鏈接分配 TCP 緩存和變量。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:
FIN = 1
,序號 seq = u
),並中止再發送數據,主動關閉 TCP 鏈接,進入 FIN-WAIT-1(終止等待1)狀態,等待 Server 的確認。ACK = 1
,確認號 ack = u + 1
,序號 seq = v
),Server 進入 CLOSE-WAIT(關閉等待)狀態,此時的 TCP 處於半關閉狀態,Client 到 Server 的鏈接釋放。注:Client 收到 Server 的確認後,進入 FIN-WAIT-2(終止等待2)狀態,等待 Server 發出的鏈接釋放報文段。
FIN = 1
,ACK = 1
,序號 seq = w
,確認號 ack = u + 1
),Server 進入 LAST-ACK(最後確認)狀態,等待 Client 的確認。ACK = 1
,seq = u + 1
,ack = w + 1
),Client 進入 TIME-WAIT(時間等待)狀態。此時 TCP 未釋放掉,須要通過時間等待計時器設置的時間 2MSL 後,Client 才進入 CLOSED 狀態。文字太亂,show you picture:
OK,至此咱們就理解了 TCP 及其三次握手和四次揮手過程,爲了方便小夥伴們形象記憶,jsliang 搞了個小故事,但願小夥伴們能加深印象:
OK,成功出糗,相信小夥伴們有了個很好的瞭解了。
那麼,咱們繼續前行探索。
最後,咱們解決第三個問題:
話很少說,一塊兒來看:
文字講解確定仍是不夠清晰的,可是 jsliang 畫了幾張圖也累了,因此我們 盜 來了一張圖:
這樣,咱們就對 瀏覽器渲染頁面過程 一清二楚
至此,咱們回顧下本身作了什麼?
-----------------小y