基於webkit和blink核心的瀏覽器討論
請求階段
- DNS解析與HTTP連線
首先Chrome搜尋自身的DNS快取,如果沒有找到或者快取失效的話,就讀hosts檔案,如果還是沒有找到,就像運營商發起DNS解析請求,獲取IP,然後三次握手建立TCP連線,連線建立完成之後就傳送HTTP請求,獲取頁面資訊,對於HTTP/1.1
支援長連線,請求頭預設帶keep-alive
,對於長連線的話,響應完成之後服務端是不會關閉HTTP連線的,所以一般會在響應頭裡新增響應完成的標誌,一般是Content-Length
Webkit網路請求
這個過程中,瀏覽器會呼叫網路模組建立連線和請求,對於Webkit
來說,有WebCro
和Webkit Ports
兩個部分,其中網路請求部分是基於作業系統的網路庫來實現的,它屬於Webkit Ports
裡的,它在不同的Webkit核心瀏覽器中的實現也不一樣,WebCro
主要是關於HTML,css解析,佈局計算和渲染的,Webkit Ports
主要是關於呼叫作業系統的模組進行網路請求,圖片解碼,視訊文字渲染以及硬體加速的功能。
DNS預取和TCP預連線
使用者在瀏覽網頁的時候,Chrome會提取網頁中的超連結,拿到域名,利用較少的CPU和頻寬解析這些域名。
<link rel="dns-prefetch" href="..." />
複製程式碼
還可能會進行TCP預連線
TCP三次握手
- 客戶端將
SYN
置為1,隨機生成一個seq
,然後把資料包傳送給服務端,客戶端進入SYN_SEND
狀態 - 服務端檢視標誌位
SYN
,如果是1,就說明是有一個客戶端請求TCP連線,然後服務端設定標誌位ACK
為1,設定ack=seq + 1
,再隨機生成一個seq
,返回給客戶端,服務端進入SYN_RECEIVED
狀態 - 客戶端收到響應之後,判斷如果
ACK
標誌位是1的話,就說明是服務端收到了請求,然後就設定ack = seq + 1
,ACK
標誌位為1,傳送給服務端 - 服務端檢測
ack
是否正確,如果正確的話,就成功建立的TCP連線
渲染階段
- 獲取HTML頁面,進行初步解析
一般情況下服務端響應的是一個HTML頁面,Chrome拿到HTML頁面之後,從頭開始解析,首先,Brower程式接收到使用者的請求之後通過UI執行緒處理,然後將任務轉交給Render程式,Renderer程式開啟一個單獨的渲染執行緒解析HTML文件,WebCro讀取HTML網頁,直譯器檢查網頁的編碼格式,使用對應的解碼器進行解析,一般編碼格式寫在meta中,國內主要有utf-8
,Unicode
,gbk
這幾類格式。
然後就可以進行詞法分析了,把HTML標籤解析成對應的token,解析的時候Webkit會過濾掉xss攻擊的程式碼。
如果解析的時候碰到了js程式碼或者script標籤,HTML會暫停解析,呼叫js引擎執行js程式碼,或者呼叫網路模組獲取js指令碼檔案,對於Chrome來說是v8,對於其他webkit核心的瀏覽器可能是JavaScriptcro,因為js引擎和webkit不是繫結在一起的。因為js程式碼雖然現在無法訪問dom,但可能會呼叫document.write修改dom結構。如果這個時候不解析HTML的話,後續的資源也無法獲取,就很虧,所以Webkit有一個預掃描和預載入機制,預掃描之後的詞語,然後提前載入需要的資源。
最終得到一個dom樹,可以通過控制檯簡單顯示這個dom樹的結構。
優化HTML解析的策略
js 載入的時候儘量使用defer或者sync,或者將js指令碼寫在HTML的下面
Webkit程式模型
Brower程式:負責顯示介面,管理各個頁面程式的建立和銷燬,所以頁面崩潰不會導致瀏覽器崩潰
Renderer程式:負責網頁的渲染不過不是一個標籤頁一個renderer程式,具體如何分配不是很清楚
GPU程式:在開啟了GPU加速時會被建立,只有一個GPU程式
- 對css進行解析
在HTML的dom樹解析完成之後,開始解析css,一般情況下瀏覽器會有一個預設的樣式,建立完了樣式規則之後,Webkit會為一些可視的節點通過標籤名和類等匹配樣式。css dom 可以確定節點物件的計算樣式
- Render Object
RenderObject包括document節點,可見節點和一些Webkit需要的節點包括了繪製的各種資訊,是經過css dom和HTML dom計算得到的,然後通過這個RenderObject使用盒模型計算dom節點的佈局,大小等屬性
- 生成RenderLayer
為了方便渲染Webkit會為一些特定的dom生成RenderLayer層次,所有的RenderObject子樹都屬於某一個RenderLayer層次 CSS position,video,canvas(呼叫GPU硬體加速),overflow,有透明度的Render Object都會生成一層RenderLayer
- 繪製
接著,Webkit開始繪製每一層的Renderlayer圖形,部分2D圖形或者文字通過CPU繪製,3D或者複雜的變換通過GPU繪製,然後合成這些圖層,這有個好處就是如果某些css變化之後我們可以繪製這一層的圖形然後再合併起來
之後頁面也就繪製完成了,接下來可能會發生迴流或者重繪
瀏覽器迴流重繪
會導致迴流的操作:
- 頁面首次渲染
- 瀏覽器視窗大小發生改變
- 元素尺寸或位置發生改變
- 元素內容變化(文字數量或圖片大小等等)
- 元素字型大小變化
- 新增或者刪除可見的DOM元素
- 啟用CSS偽類(例如::hover)
- 查詢某些屬性或呼叫某些方法
一些常用且會導致迴流的屬性和方法:
clientWidth、clientHeight、clientTop、clientLeft
offsetWidth、offsetHeight、offsetTop、offsetLeft
scrollWidth、scrollHeight、scrollTop、scrollLeft
scrollIntoView()、scrollIntoViewIfNeeded()
getComputedStyle()
getBoundingClientRect()
scrollTo()
複製程式碼
如何避免迴流
- 避免頻繁操作DOM,建立一個documentFragment,在它上面應用所有DOM操作,最後再把它新增到文件中。
- 也可以先為元素設定display: none,操作結束後再把它顯示出來。因為在display屬性為none的元素上進行的DOM操作不會引發迴流和重繪。
參考資料:
Webkit技術內幕
簡述TCP的三次握手過程
HTTP keep-alive詳解
瀏覽器的迴流與重繪 (Reflow & Repaint)