最近看了< webkit技術內幕 >,雖然並不能徹底看懂,可是對瀏覽器的渲染機制也算是有了一個比較完整的認識。css
咱們從瀏覽器地址欄輸入網址開始到web頁面被完整的呈如今眼前,大概的通過了這樣一個過程:網址被DNS解析爲IP地址 -> 經過IP地址創建TCP鏈接 -> 發送HTTP請求 -> 服務器處理請求並返回響應 -> 瀏覽器解析渲染頁面 -> 斷開TCP鏈接web
但是瀏覽器是怎麼去解析渲染頁面的呢?這裏就要涉及到瀏覽器的內核,也就是瀏覽器的渲染引擎(嚴格來講應該是渲染引擎 + Javascript引擎),頁面的渲染工做即是由渲染引擎完成的。須要注意瀏覽器和瀏覽器內核是不一樣的概念,瀏覽器指的是 Chrome、Firefox 等,而瀏覽器內核則是 Blink、Gecko 等,瀏覽器內核只負責渲染,GUI 及網絡鏈接等跨平臺工做則是瀏覽器實現的。主流的渲染引擎包括Trident、Gecko、Webkit,它們分別是IE、火狐和Chrome的內核(2013年,Google宣佈了Blink內核,它實際上是從Webkit複製出去的)。通常渲染引擎主要包括HTML解釋器、CSS解釋器、Javascript引擎、佈局、繪圖等模塊。固然這些模塊還依賴不少其餘的基礎模塊。瀏覽器
咱們先簡單的瞭解一下渲染引擎各個主模塊所作的工做(如下全部的介紹以 webkit 爲介紹對象)服務器
HTML解釋器 :HTML解釋器的工做就是將網絡或者本地磁盤獲取到的HTML網頁和資源從字節流解釋成DOM樹的結構(首先是字節流,通過解碼以後是字符流,而後經過詞法分析器會被解釋成詞語(TOKENS),通過語法分析器構建成節點,最後這些節點被組建成一顆DOM樹)網絡
CSS解釋器 :CSS字符串被CSS解釋器處理後變成渲染引擎的內部規則表示。(樣式規則創建完成以後,webkit會保存規則結果,當DOM的節點創建以後,webkit會爲可視化節點選擇合適的樣式信息,即做樣式規則匹配)併發
Javascript引擎 :將Javascript代碼處理並執行,一個Javascript引擎能夠包括如下幾個部分 ->dom
編譯器 -> 主要工做是將源代碼編譯成抽象語法樹,在某些引擎中還包括將抽象語法樹轉換爲字節碼(JavascriptCore 引擎)。異步
解釋器 -> 在某些引擎中,解釋器主要是接收字節碼,解釋執行字節碼,同時也依賴垃圾回收機制等。async
JIT工具 -> 將字節碼或者抽象語法樹轉換爲本地代碼 (V8 引擎)ide
垃圾回收器和分析工具
佈局 :計算RenderObject對象的位置、大小等信息
繪圖 :將構建好的渲染內部表示模型使用圖形庫繪製出來
頁面的最終渲染即是上述模塊綜合處理的結果。根據數據的流向,這裏能夠把渲染的過程分爲三個階段,第一個階段是從網頁的url到構建完dom樹,第二個階段是從dom樹到構建完webkit的繪圖上下文,第三個階段是從繪圖上下文到生成最後的圖像。
上圖描述的是網頁 url 到構建完DOM樹的整個過程,數字表示的是基本順序,不過也不是嚴格一致,由於這個過程可能會有所重複或者交叉。
具體的過程以下:
1 當用戶輸入網頁url的時候,webkit調用資源加載器加載對應的網頁。
2 加載器依賴網絡模塊創建鏈接,發生請求並接收響應。
3 webkit接收到各類網頁或者資源的數據,其中某些資源多是同步或者異步的方式獲取的。
4 網頁被交給HTML解釋器解釋成一系列詞語(Token)
5 解釋器根據詞語構建節點(Node),造成DOM樹。
6 若是節點是Javascript代碼的話,調用Javascript引擎解釋執行。
7 Javascript代碼可能會修改DOM樹的結構。
8 若是有節點須要依賴其餘的資源,好比圖片、css、視頻等,調用資源加載器來加載它們,不過這個過程是異步的,不會阻礙當前DOM樹的繼續構建。若是是Javascript資源 url (沒有標記異步方式),則須要中止當前DOM樹的構建,直到Javascript的資源被加載並被Javascript引擎解釋執行後才繼續DOM樹的建立。
在上述過程中,網頁在加載和被渲染完畢的這一段過程會相繼發出「DOMContent」事件和「onload」事件,分別表示DOM樹構建完畢以及DOM樹及其全部依賴的資源都加載完畢。通常來講「DOMContent」事件會先於「onload」事件發生。
接下來就是Webkit藉助 css 和 dom 樹構建RenderObject樹直到繪圖上下文。
1 css文件被css解釋器解釋爲內部表示結果
2 css解釋器完成工做以後在dom樹上附加解釋後的樣式信息,這就是RenderObject樹
3 RenderObject節點被建立的同時,webkit會根據網頁的層次結構建立RenderLayer樹,同時建立一個虛擬的繪圖上下文。
RenderObject樹的創建並不會致使DOM樹被銷燬,上述四個內部表示結構直到網頁被銷燬都一直存在。
最後就是根據繪圖上下文來生成最終圖像了,這一過程主要依賴2D和3D圖形庫。
生成過程的簡單描述以下圖所示:
整個渲染的流程至此即是結束了,從渲染的整個過程當中咱們也能夠發現,原來 CSS、圖片、視頻等資源的加載是異步的,同 dom 樹的構建是併發的,而 Javascript 資源的加載和執行倒是同步的,這個過程會阻塞 dom 樹的構建,同時固然也會阻礙後續資源(好比圖片資源)的下載,由於這時候webkit對須要什麼資源一無所知,這致使了資源不能併發下載這一嚴重的性能問題。(其實對於這樣的狀況,webkit採起了資源預掃描和預加載的機制來實現資源的併發下載而不被 Javascript 的執行所阻礙,但仍要避免這種狀況的出現,由於並非全部的渲染引擎都作了如此的考慮)。因此從這裏咱們也能夠獲得一些關於渲染優化方面的啓示:
1 最好把腳本文件放在HTML文件的末尾,這樣就不會阻塞 dom 樹的構建和資源的併發下載
2 若是非要把腳本文件放在HTML文件的前面,請給 script 標籤添加 「async」 屬性或者 「defer」 屬性。前者代表這是一段能夠異步執行的 Javascript 代碼,然後者表示在加載完 Javascript 以後延遲到 dom 樹構建就緒以後再執行代碼。若是同時添加 「async」 和 「defer」 ,在同時支持這兩個屬性的瀏覽器中 「async」 會覆蓋掉 「defer」。須要注意的一點是,若是當前添加 「async」 屬性的腳本文件依賴有其餘的腳本文件,可能會致使添加 「async」 屬性的腳本文件先於依賴腳本執行,即便依賴腳本先於添加 「async」 屬性的腳本文件被定義。