1. 程式人生 > >面試題之從敲入 URL 到瀏覽器渲染完成

面試題之從敲入 URL 到瀏覽器渲染完成

前言

小汪最近在看【WebKit 技術內幕】一書,說實話,這本書寫的太官方了,不通俗易懂。但是看完書,對瀏覽器核心的 WebKit 有了進一步的瞭解,所以從瀏覽器核心出發,寫這篇文章以記錄學到的知識,以加深對 WebKit 的理解。

相信很多開發人員在面試時都遇到這個問題,這道題可說是非常非常難的,因為深度可以非常深,廣度可以非常廣。這題是非常能考查一個前端開發人員的知識體系的題目。

寫這篇文章的時候,邊寫邊覺得難 !!!

1. 大致過程

當你這樣子回答的時候:

  • 使用者輸入 url 地址,瀏覽器查詢 DNS 查詢對應的請求 IP 地址
  • 建立 TCP 連線
  • 瀏覽器向伺服器傳送 http 請求,如果伺服器段返回以 301 之類的重定向,瀏覽器根據相應頭中的 location 再次傳送請求
  • 伺服器端接受請求,處理請求生成 html 程式碼,返回給瀏覽器,這時的 html 頁面程式碼可能是經過壓縮的
  • 瀏覽器接收伺服器響應結果,如果有壓縮則首先進行解壓處理,緊接著就是頁面解析渲染
  • 解析該過程分為:解析 HTML,構建 DOM 樹,DOM 樹與 CSS 樣式進行附著構造呈現樹,佈局、繪製

雖然這大致的過程是對的,但回答不上細節 !深度不夠!!!

面試官給你的臉色是:“很遺憾,這不是我們要的回答 ! ”

2. 詳細過程

下面讓我們扒下各個過程細節的外衣,坦誠相見吧 !

2.1 輸入地址

瀏覽器引入了 DNS 預取技術。它是利用現有的 DNS 機制,提前解析網頁中可能的網路連線。

當我們開始在瀏覽器中輸入網址的時候,瀏覽器其實就已經在智慧的匹配可能得 url 了。它會從歷史記錄,書籤等地方,找到已經輸入的字串可能對應的 url ,找到同輸入的地址很匹配的項,然後給出智慧提示,讓你可以補全 url 地址。使用者還沒有按下 enter 鍵, 瀏覽器已經開始使用 DNS 預取技術解析該域名了。

對於 chrome 的瀏覽器,如果有該域名相關的快取,它會直接從快取中把網頁展示出來,就是說,你還沒有按下 enter,頁面就出來了。如果沒有快取,就還是會重新請求資源。

2.2 查詢 DNS 查詢對應的請求 IP 地址

假設輸入 www.baidu.com,大概過程:

  • 瀏覽器搜尋自己的 DNS 快取。
  • 在瀏覽器快取中沒找到,就在作業系統快取中查詢,這一步中也會查詢本機的 hosts 看看有沒有對應的域名對映。
  • 在系統中也沒有的話,就到你的路由器來查詢,因為路由器一般也會有自己的 DNS 快取。
  • 若沒有,則作業系統將域名傳送至 本地域名伺服器——遞迴查詢方式,本地域名伺服器 查詢自己的 DNS 快取,查詢成功則返回結果,否則,採用迭代查詢方式。本地域名伺服器一般都是你的網路接入伺服器商提供,比如中國電信,中國移動。
  • 本地域名伺服器 將得到的 IP 地址返回給作業系統,同時自己也將 IP 地址快取起來。
  • 作業系統將 IP 地址返回給瀏覽器,同時自己也將 IP 地址快取起來,以備下次別的使用者查詢時,可以直接返回結果,加快網路訪問。
  • 至此,瀏覽器已經得到了域名對應的 IP 地址。

參考文章:

2.3 建立 TCP 連線

TCP 是一種面向有連線的傳輸層協議。它可以保證兩端(傳送端和接收端)通訊主機之間的通訊可達。它能夠處理在傳輸過程中丟包、傳輸順序亂掉等異常情況;此外它還能有效利用寬頻,緩解網路擁堵。

三次握手的步驟:(抽象派)

客戶端:hello,你是server麼?
服務端:hello,我是server,你是client麼
客戶端:yes,我是client

在 TCP 連線建立完成之後就可以傳送 HTTP 請求了。

然後,待到斷開連線時,需要進行四次揮手(因為是全雙工的,所以需要四次揮手)

四次揮手的步驟:(抽象派)

主動方:我已經關閉了向你那邊的主動通道了,只能被動接收了
被動方:收到通道關閉的資訊
被動方:那我也告訴你,我這邊向你的主動通道也關閉了
主動方:最後收到資料,之後雙方無法通訊

2.4 伺服器收到請求並響應 HTTP 請求

在接收和解釋請求訊息後,伺服器返回一個HTTP響應訊息。

HTTP 響應由三個部分組成,分別是:狀態行、訊息報頭、響應正文。

狀態程式碼:由三位數字組成,第一個數字定義了響應的類別,且有五種可能取值:

  • 1xx:指示資訊--表示請求已接收,繼續處理
  • 2xx:成功--表示請求已被成功接收、理解、接受
  • 3xx:重定向--要完成請求必須進行更進一步的操作
  • 4xx:客戶端錯誤--請求有語法錯誤或請求無法實現
  • 5xx:伺服器端錯誤--伺服器未能實現合法的請求

常見狀態程式碼、狀態描述、說明:

  • 200 OK :客戶端請求成功
  • 400 Bad Request :客戶端請求有語法錯誤,不能被伺服器所理解
  • 401 Unauthorized :請求未經授權,這個狀態程式碼必須和WWW-Authenticate報頭域一起使用
  • 403 Forbidden :伺服器收到請求,但是拒絕提供服務
  • 404 Not Found :請求資源不存在,eg:輸入了錯誤的URL
  • 500 Internal Server Error :伺服器發生不可預期的錯誤
  • 503 Server Unavailable :伺服器當前不能處理客戶端的請求,一段時間後可能恢復正常

HTTP訊息報頭包括:普通報頭、請求報頭、響應報頭、實體報頭。具體不作介紹。

響應正文:就是伺服器返回的資源的內容

2.5 瀏覽器接收伺服器響應結果並處理

在瀏覽器沒有完整接受全部HTML文件時,它就已經開始顯示這個頁面了,不同瀏覽器可能解析的過程不太一樣,這裡我們只介紹 WebKit 的渲染過程。

渲染步驟大致可以分為以下幾步:

1. 解析HTML,構建 DOM 樹

2. 解析 CSS ,生成 CSS 規則樹

3. 合併 DOM 樹和 CSS 規則,生成 render 樹

4. 佈局 render 樹( Layout / reflow ),負責各元素尺寸、位置的計算

5. 繪製 render 樹( paint ),繪製頁面畫素資訊

6. 瀏覽器會將各層的資訊傳送給 GPU,GPU 會將各層合成( composite ),顯示在螢幕上

其中每個解釋的過程中,WebKit 都提供了很多相關的類來一步一步地解釋對應的內部模組,這裡面不做詳細描述。

下面根據上面的大致過程來一步步細解。

2.5.1 構造 DOM 樹

瀏覽器在解析html檔案時, 是WebKit 中的 HTML 直譯器的將網路或者本地磁盤獲取的 HTML 網頁和資源從位元組流解釋成 DOM 樹結構。具體過程如下 :

在 WebKit 中這一過程如下:首先是位元組流,經過解碼之後是字元流,然後通過詞法分析器會被解釋成詞語(Tokens),之後經過語法分析器構建成節點,最後這些節點被組建成一棵 DOM 樹。

瀏覽器在解析html檔案過程中,會 ”自上而下“ 載入,並在載入過程中進行解析渲染。在解析過程中,如果遇到請求外部資源時,如圖片、外鏈的CSS、iconfont等,請求過程是非同步的,並不會影響html文件進行載入,且統一交由 Browser 程序來處理,這使得資源在不同網頁間的共享變得很容易。

HTML 的解釋、佈局和渲染等工作基本上就是工作在渲染執行緒完成的(這不是絕對的)。因為 DOM 樹只能在渲染執行緒上建立和訪問,這也就是說構建 DOM 樹的過程只能在渲染執行緒中進行,但是,從字元到詞語這個階段可以交給另外的單獨的執行緒來做。

而且因為有 DNS 預取技術,當用戶正在瀏覽當前網頁的時候,Chromium 提取網頁中的超連結,將域名抽取出來,利用比較少的 CPU 和網路頻寬來解析這些域名或者 IP 地址,這樣一來,使用者根本感覺不到這一過程。當用戶單擊這些連結的時候,可以節省不少時間,特別在域名解析比較慢的時候,效果特別明顯。

解析過程中,瀏覽器首先會解析 HTML 檔案構建 DOM 樹,然後解析 CSS 檔案構建 Render樹,等到 Render 樹構建完成後,瀏覽器開始佈局 Render 樹並將其繪製到螢幕上。

2.5.2 解釋 CSS

CSS 解釋過程是指從 CSS 字串 經過 CSS 直譯器 處理後變成渲染引擎內部規則的表示過程。

生成樣式規則之後,會進行樣式規則匹配,WebKit 會為其中的一些節點(只限於可視節點)選擇合適的樣式資訊,規則的匹配則是由 ElementRuleCollector 類來計算並獲得,它根據元素的屬性等,並從 DocumentRuleSets 類中獲取規則集合,依次按照 ID、類別、標籤等選擇器資訊逐次匹配獲得元素的樣式。

最後,WebKit 對這些規則進行排序。對於該元素需要的樣式屬性,WebKit 選擇從高優先順序規則中選取,並將樣式屬性值返回。

從整個網頁的載入和渲染過程來看,CSS 解釋和規則匹配處於 DOM 樹建立之後,RenderObject 樹建立之前,CSS 直譯器解釋後的結果會儲存起來,然後 RenderObject 樹基於該結果來進行規範匹配和佈局計算。當網頁有使用者互動或者動畫等動作的時候,通過 CSSDOM 等技術,JavaScript 程式碼同樣可以非常方便地修改 CSS 程式碼,WebKit 此時需要重新解釋樣式並重復以上這一過程。

2.5.3 渲染過程遇到 JavaScript

當文件載入過程中遇到 js 檔案,html 文件會掛起渲染(載入解析渲染同步)的執行緒,不僅要等待文件中 js 檔案載入完畢,還要等待解析執行完畢,才可以恢復 html 文件的渲染執行緒。因為 JS 有可能會修改 DOM,最為經典的 document.write,這意味著,在 JS 執行完成前,後續所有資源的下載可能是沒有必要的,這是 js 阻塞後續資源下載的根本原因。所以我們平時的程式碼中,js 是放在 html 文件末尾的。

而且當遇到執行 JavaScript 程式碼的時候,WebKit 先暫停當前 JavaScript 程式碼的執行,使用預先掃描器 HTMLPreloadScanner 類來掃描後面的詞語。如果 WebKit 發現它們需要使用其他資源,那麼使用預資源載入器 HTMLPreloadScanner 類來發送請求,在這之後,才執行 JavaScript 程式碼。預先掃描器本身並不建立節點物件,也不會構建 DOM 樹,所以速度比較快。

當 DOM 樹構建完之後,WebKit 觸發 “DOMContentLoaded” 事件,註冊在該事件上的 JavaScript 函式會被呼叫。當所在資源都被載入完之後,WebKit 觸發 “onload” 事件。

WebKit 將 DOM 樹建立過程中需要執行的 JavaScript 程式碼交由 HTMLScriptRunner 類來負責。工作方式很簡單,就是利用 JavaScript 引擎來執行 Node 節點中包含的程式碼。

JS 的解析是由瀏覽器中的 JavaScript 引擎完成的。JS是單執行緒執行,也就是說,在同一個時間內只能做一件事,所有的任務都需要排隊,前一個任務結束,後一個任務才能開始。但是又存在某些任務比較耗時,如 IO 讀寫等,所以需要一種機制可以先執行排在後面的任務,這就是:同步任務(synchronous)和非同步任務(asynchronous)。

JS 的執行機制就可以看做是一個主執行緒加上一個任務佇列(task queue)。同步任務就是放在主執行緒上執行的任務,非同步任務是放在任務佇列中的任務。所有的同步任務在主執行緒上執行,形成一個執行棧; 非同步任務有了執行結果就會在任務佇列中放置一個事件;指令碼執行時先依次執行執行棧,然後會從任務佇列裡提取事件,執行任務佇列中的任務,這個過程是不斷重複的,所以又叫做事件迴圈(Event loop)。

2.5.4 渲染合成 Render 樹

HTML 經過 WebKit 解釋之後,生成 DOM 樹。在 DOM 樹構建完成之後,WebKit 會為 DOM 樹節點構建 RenderObject 樹,再通過 RenderObject 樹構建出 RenderLayer 樹。

RenderObject 樹是基於 DOM 樹建立起來的一棵新樹,是為了佈局計算和渲染等機制而構建的一種新的內部表示。RenderObject 樹節點和 DOM 節點不是一一對應關係,因為有可視節點(常用的 div img 標籤等)與不可視節點(如 head、meta 標籤),不可視節點是不會構成 RenderObject 樹的。

網頁是有層次結構的,可以分層的,一是為了方便設定網頁的層次,二是為了 WebKit 處理上的便利,為了簡化渲染的邏輯。

而且 RenderLayer 節點和 RenderObject 節點不是一一對應關係,而是一對多的關係。

2.5.5 佈局

當 WebKit 建立 RenderObject 物件之後,每個物件是不知道自己的位置、大小等資訊的,WebKit 根據框模型來計算它們的位置,大小等資訊的過程稱為佈局計算。

佈局計算是一個遞迴的過程,因為一個節點的大小通常需要先計算它的子女節點的位置,大小等資訊。

當用戶 網頁的動畫、翻滾網頁、JavaScript 程式碼通過 CSSDOM 等操作時還會有重新佈局。

2.5.6 繪圖

在 WebKit 中,繪圖操作就是繪圖上下文,所有繪圖的操作都是在該上下文中來進行的。

繪圖上下文可以分成兩種型別:

一是 2D 圖形上下文(GraphicsContext),用來繪製 2D 圖形的的上下文;

二是 3D 繪圖上下文,是用來繪製 3D 圖形的上下文。

2D 繪圖上下文具體的作用:提供基本繪圖單元的繪製介面以及設定繪圖的樣式。繪圖介面包括畫點,畫線、畫圖片、畫多邊形、畫文字等,繪圖樣式包括顏色、線寬、字號大小、漸變等。

關於 3D 繪圖上下文,它的主要用處是支援 CSS3D、WebGL 等。

網頁的渲染方式,有三種方式,一是軟體渲染,二是硬體加速渲染,三可以說是混合模式。

如果繪圖操作使用 CPU 來完成,稱之為軟體繪圖。

如果繪圖操作由 GPU 來完成,稱之為 GPU 硬體加速繪圖。

理想情況下,每個層都有個繪製的儲存區域,這個儲存區域用來儲存繪圖的結果。最後,需要將這些層的內容合併到同一個影象之中,可以稱之為合成(Compositing),使用了合成技術的渲染稱之為合成化渲染。

所以,在完成構建 DOM 樹之後,WebKit 會呼叫繪圖操作、軟體渲染或者硬體加速渲染或者兩者都有,將模型繪製出來,呈現在螢幕上。至此,瀏覽器渲染完成。

最後

現在,當面試官再問你 “從敲入 URL 到瀏覽器渲染完成” 的時候,你的內心是不是這樣的 ?

你以為本文就這麼結束了 ? 精彩在後面 !!!

對 全棧開發 有興趣的朋友可以掃下方二維碼關注我的公眾號

我會不定期更新有價值的內容。

微信公眾號:愛寫bugger的阿拉斯加分享 前端開發、後端開發 等相關的技術文章,熱點資源,全棧程式設計師的成長之路。

關注公眾號並回復 福利 便免費送你六套視訊資源,絕對乾貨。

愛寫bugger的阿拉斯加