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 後發生的事情 也關注一下,從而引出 DNS、TCP 等知識點,最終串起來構成本文的輪廓,方便 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>
複製程式碼
很好,相信小夥伴們已經看完了程式碼,下面我們看看它的演示:
這時候,我們可以丟擲防抖的概念了:
- 防抖:任務頻繁觸發的情況下,只有任務觸發的間隔超過指定間隔的時候,任務才會執行。
結合上面的程式碼,我們可以瞭解到,在觸發點選事件後,如果使用者再次點選了,我們會清空之前的定時器,重新生成一個定時器。意思就是:這件事兒需要等待,如果你反覆催促,我就重新計時!
空講無益,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 實現:
看完程式碼和 GIF 實現,我們可以明白,節流即是:
- 節流:指定時間間隔內只會執行一次任務。
那麼,節流在工作中的應用?
- 懶載入要監聽計算滾動條的位置,使用節流按一定時間的頻率獲取。
- 使用者點選提交按鈕,假設我們知道介面大致的返回時間的情況下,我們使用節流,只允許一定時間內點選一次。
這樣,在某些特定的工作場景,我們就可以使用防抖與節流來減少不必要的損耗。
那麼問題來了,假設面試官聽到你這句話,是不是會接著問一句:“為什麼說上面的場景不節制會造成過多損耗呢?”
OK,這就涉及到瀏覽器渲染頁面的機制了……
四 重繪與迴流
在說瀏覽器渲染頁面之前,我們需要先了解兩個點,一個叫 瀏覽器解析 URL,另一個就是本章節將涉及的 重繪與迴流:
- 重繪(repaint):當元素樣式的改變不影響佈局時,瀏覽器將使用重繪對元素進行更新,此時由於只需要 UI 層面的重新畫素繪製,因此損耗較少。
常見的重繪操作有:
- 改變元素顏色
- 改變元素背景色
- more ……
- 迴流(reflow):又叫重排(layout)。當元素的尺寸、結構或者觸發某些屬性時,瀏覽器會重新渲染頁面,稱為迴流。此時,瀏覽器需要重新經過計算,計算後還需要重新頁面佈局,因此是較重的操作。
常見的迴流操作有:
- 頁面初次渲染
- 瀏覽器視窗大小改變
- 元素尺寸/位置/內容發生改變
- 元素字型大小變化
- 新增或者刪除可見的 DOM 元素
- 啟用 CSS 偽類(:hover……)
- more ……
- 重點:迴流必定會觸發重繪,重繪不一定會觸發迴流。重繪的開銷較小,迴流的代價較高。
看到這裡,小夥伴們可能有點懵逼,你剛剛還跟我講著 防抖與節流 ,怎麼一下子跳到 重繪與迴流 了?
OK,賣個關子,先看下面場景:
- 介面上有個 div 框,使用者可以在 input 框中輸入 div 框的一些資訊,例如寬、高等,輸入完畢立即改變屬性。但是,因為改變之後還要隨時儲存到資料庫中,所以需要呼叫介面。如果不加限制……
看到這裡,小夥伴們可以將一些字眼結合起來了:為什麼需要 節流,因為有些事情會造成瀏覽器的 迴流,而 迴流 會使瀏覽器開銷增大,所以我們通過 節流 來防止這種增大瀏覽器開銷的事情。
形象地用圖來說明:
這樣,我們就可以形象的將 防抖與節流 與 重繪與迴流 結合起來記憶起來。
那麼,在工作中我們要如何避免大量使用重繪與迴流呢?:
- 避免頻繁操作樣式,可彙總後統一一次修改
- 儘量使用 class 進行樣式修改,而不是直接操作樣式
- 減少 DOM 的操作,可使用字串一次性插入
OK,至此我們就講完兩個部分了,那麼問題又來了:“瀏覽器渲染過程中,是不是也有重繪與迴流?”“從瀏覽器輸入 URL 到渲染成功的過程中,究竟發生了什麼?”
我們,繼續深入探索……
五 瀏覽器解析 URL
為了能讓我們的知識層面看起來更有深度,我們應該考慮下面兩個問題了:
- 從瀏覽器輸入 URL 到渲染成功的過程中,究竟發生了什麼?
- 瀏覽器渲染過程中,發生了什麼,是不是也有重繪與迴流?
OK,興致來了,我們就先從 瀏覽器解析 URL 看起,先來看看當使用者輸入 URL,到瀏覽器呈現給使用者頁面,經歷了以下過程:
- 版本 A:
- 使用者輸入 URL 地址。
- 對 URL 地址進行 DNS 域名解析。
- 建立 TCP 連線(三次握手)。
- 瀏覽器發起 HTTP 請求報文。
- 伺服器返回 HTTP 響應報文。
- 關閉 TCP 連線(四次揮手)。
- 瀏覽器解析文件資源並渲染頁面。
講到這裡,突然想起一個對話:
學生:“老師,這門課的考試重點是什麼?”
老師:“全都是重點!”
enm...老師會不會被打我不知道,但是 jsliang 這樣寫會被懟我就清楚,所以,我們還是結合上面的圖,進一步勾勒我們的結構:
很好,jsliang 感覺自己的畫圖技術又進了一步~
①:雖然很感激網上有那麼多的文章可以參考,但是在我查了二十來篇文章後,jsliang 覺得這部分十有八九有問題撒,問了些小夥伴,它們有的說對,有的說錯。不過,不妨礙小夥伴們繼續往下看哈。
②:為了避免出簍子,下面貼出另外一個版本,小夥伴們可以在評論區說出你支援哪個版本哈:
- 版本 B
- 使用者輸入 URL 地址。
- 對 URL 地址進行 DNS 域名解析。
- 進行 TCP 連線。
- 進行 HTTP 報文的請求與響應。
- 瀏覽器解析文件資源並渲染頁面。
在這裡我們可以清晰的瞭解到從 使用者輸入 URL,到瀏覽器呈現給使用者頁面,經歷了哪些過程。
那麼剩下的就簡單了:
- 什麼是 DNS 解析,它是怎麼個流程?
- 什麼是 TCP 三次握手,什麼是 TCP 四次揮手,它們的流程是怎樣的?
- 瀏覽器解析文件資源並渲染頁面是個怎樣的流程?
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 經歷了以下步驟:
- 瀏覽器根據地址,在自身快取中查詢 DNS(域名伺服器) 中的解析記錄。如果存在,則直接返回 IP 地址;如果不存在,則查詢作業系統中的 hosts 檔案是否有該域名的 DNS 解析記錄,如果有就返回。
- 在條件 1 中的瀏覽器快取或者作業系統的 hosts 檔案中都沒有這個域名的 DNS 解析記錄,或者已經過期,則向域名伺服器發起請求解析這個域名。
- 先向本地域名伺服器中請求,讓它解析這個域名,如果解析不了,則向根域名伺服器請求解析。
- 根伺服器給本地域名伺服器返回一個主域名伺服器。
- 本地域名伺服器向主域名伺服器發起解析請求。
- 主域名伺服器接收到解析請求後,查詢並返回域名對應的域名伺服器的地址。
- 域名伺服器會查詢儲存的域名和 IP 的對映關係表,返回目標 IP 記錄以及一個 TTL(Time To Live)值。
- 本地域名伺服器接收到 IP 和 TTL 值,進行快取,快取的時間由 TTL 值控制。
- 將解析的結果返回給使用者,使用者根據 TTL 值快取在本地系統快取中,域名解析過程結束。
看文字總是難以理解的,跟著 jsliang 畫張圖過一遍,就感覺清晰了:
七 TCP 三次握手與四次揮手
然後,我們解決第二個問題:
- 什麼是 TCP 三次握手,什麼是 TCP 四次揮手,它們的流程是怎樣的?
什麼是 TCP 呢?TCP(Transmission Control Protocol 傳輸控制協議)是一種面向連線的、可靠的、基於位元組流的傳輸層通訊協議。
簡單來說,它的作用就是將資料流從一臺主機可靠地傳輸到另一臺主機。
至於具體的工作原理,這裡暫時涉及不到,我們目前只想知道兩個點:三次握手與四次揮手。
- 三次握手:
- 第一次握手:起初兩端都處於 CLOSED 關閉狀態,Client 將標誌位 SYN 置為 1,隨機產生一個值
seq = x
,並將該資料包傳送給 Server,Client 進入 SYN-SENT 狀態,等待 Server 確認。 - 第二次握手:Server 收到資料包後由標誌位
SYN = 1
得知 Client 請求建立連線,Server 將標誌位 SYN 和 ACK 都置為 1,ack = x + 1
,隨機產生一個值seq = y
,並將該資料包傳送給Client以確認連線請求,Server 進入SYN-RCVD
狀態,此時作業系統為該 TCP 連線分配 TCP 快取和變數。 - 第三次握手: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:
- 四次揮手:
- 第一次揮手:Client 的應用程式先向其 TCP 發出連線釋放報文段(
FIN = 1
,序號seq = u
),並停止再傳送資料,主動關閉 TCP 連線,進入 FIN-WAIT-1(終止等待1)狀態,等待 Server 的確認。 - 第二次揮手:Server 收到連線釋放報文段後即發出確認報文段,(
ACK = 1
,確認號ack = u + 1
,序號seq = v
),Server 進入 CLOSE-WAIT(關閉等待)狀態,此時的 TCP 處於半關閉狀態,Client 到 Server 的連線釋放。
注:Client 收到 Server 的確認後,進入 FIN-WAIT-2(終止等待2)狀態,等待 Server 發出的連線釋放報文段。
- 第三次揮手:Server 已經沒有要向 Client 發出的資料了,Server 發出連線釋放報文段(
FIN = 1
,ACK = 1
,序號seq = w
,確認號ack = u + 1
),Server 進入 LAST-ACK(最後確認)狀態,等待 Client 的確認。 - 第四次揮手:Client 收到 Server 的連線釋放報文段後,對此發出確認報文段(
ACK = 1
,seq = u + 1
,ack = w + 1
),Client 進入 TIME-WAIT(時間等待)狀態。此時 TCP 未釋放掉,需要經過時間等待計時器設定的時間 2MSL 後,Client 才進入 CLOSED 狀態。
文字太亂,show you picture:
OK,至此我們就理解了 TCP 及其三次握手和四次揮手過程,為了方便小夥伴們形象記憶,jsliang 搞了個小故事,希望小夥伴們能加深印象:
- 三次握手 + 四次揮手形象記憶:
- jsliang:(對妹子發起微信好友申請)“你好,我可以加你好友嗎?” —— 第一次握手
- 妹子:(通過稽核)“你好,很高興認識你~” —— 第二次握手
- jsliang:“你好,我叫樑峻榮,前端折騰小能手……” —— 第三次握手
- ……(聊天內容)
- …………(聊天內容)
- ………………(聊天內容)
- …………(聊天內容)
- ……(聊天內容)
- jsliang:(感冒拍了張紙簍都是紙巾的圖)“啊,好難受今天。” —— 第一次揮手
- 妹子:“臥槽,你好惡心!” —— 第二次揮手
- 妹子:“我們還是當不認識吧,互刪了,謝謝!” —— 第三次揮手
- jsliang:(呆)“不是,你聽我說!” —— 第四次揮手
- 妹子:(果斷刪除好友) —— CLOSED
- jsliang:(!“我今天感冒了。” 妹子開啟了好友驗證,你還不是她好友。請先傳送好友驗證請求,對方驗證通過後,才能聊天。) ——— CLOSED
OK,成功出糗,相信小夥伴們有了個很好的瞭解了。
那麼,我們繼續前行探索。
參考文獻 1:《TCP三次握手和四次揮手過程》
參考文獻 2:《TCP的三次握手與四次揮手(詳解+動圖)》
八 瀏覽器渲染頁面
最後,我們解決第三個問題:
- 瀏覽器解析文件資源並渲染頁面是個怎樣的流程?
話不多說,一起來看:
- 瀏覽器通過 HTMLParser 根據深度遍歷的原則把 HTML 解析成 DOM Tree。
- 瀏覽器通過 CSSParser 將 CSS 解析成 CSS Rule Tree(CSSOM Tree)。
- 瀏覽器將 JavaScript 通過 DOM API 或者 CSSOM API 將 JS 程式碼解析並應用到佈局中,按要求呈現響應的結果。
- 根據 DOM 樹和 CSSOM 樹來構造 render Tree。
- layout:重排(也可以叫回流),當 render tree 中任一節點的幾何尺寸發生改變,render tree 就會重新佈局,重新來計算所有節點在螢幕的位置。
- repaint:重繪,當 render tree 中任一元素樣式屬性(幾何尺寸沒改變)發生改變時,render tree 都會重新畫,比如字型顏色,背景等變化。
- paint:遍歷 render tree,並調動硬體圖形 API 來繪製每個節點。
文字講解肯定還是不夠清晰的,但是 jsliang 畫了幾張圖也累了,所以我們們 盜 來了一張圖:
這樣,我們就對 瀏覽器渲染頁面過程 一清二楚啦~
參考文獻:《一篇文章搞定前端面試》
九 總結
至此,我們回顧下自己做了什麼?
- 我們在工作中碰到一些問題,這些問題會卡住頁面,於是我們查資料,知道想要減少瀏覽器的開銷,我們就需要使用 防抖與節流。
- 使用 防抖與節流 解決完問題後,我們好奇為什麼會有這樣的操作,於是我們深入瞭解了下 重繪與迴流。
- 重繪與迴流 只告訴了我們瀏覽器在 CSS 上的渲染,我們需要進一步瞭解 瀏覽器渲染頁面 的詳細過程,但洋蔥還是要一層一層剝開的,所以我們需要從 瀏覽器解析 URL 開始瞭解。
- 在 瀏覽器解析 URL 中,我們順帶了解下 DNS 域名解析、TCP 三次握手與四次揮手 這兩個知識點。
- 最後,我們終於知道了 瀏覽器渲染頁面 是怎麼一回事。
綜上,如果我們僅僅是需要關注面試的一個點,我們很可能因為不知頭尾,而被面試官問得啞口無言。
但是,如果我們知道一個知識點,並對其進行思路發散,深入學習,相信面試官問起來的時候,小夥伴們就可以侃侃而談,而不會被問地體無完膚了!
最後祝小夥伴們找到合適的滿意的工作~
十 參考文獻
- 《函式防抖和節流》
- 《節流 & 防抖》
- 《JS奇淫巧技:防抖函式與節流函式》
- 《閒聊 JS 中的 apply 和 call》
- 《js 中 arguments 的用法》
- 《防抖和節流的應用場景和實現》
- 《網頁解析的全過程(輸入url到展示頁面)》
- 《瀏覽器渲染頁面過程剖析》
- 《一篇文章搞定前端面試》
jsliang 廣告推送:
也許小夥伴想了解下雲伺服器
或者小夥伴想買一臺雲伺服器
或者小夥伴需要續費雲伺服器
歡迎點選 雲伺服器推廣 檢視!
jsliang 的文件庫 由 樑峻榮 採用 知識共享 署名-非商業性使用-相同方式共享 4.0 國際 許可協議進行許可。
基於github.com/LiangJunron…上的作品創作。
本許可協議授權之外的使用許可權可以從 creativecommons.org/licenses/by… 處獲得。