瀏覽器是如何工作的——學(xué)習(xí)整理(一)

本文為學(xué)習(xí)整理筆記,原文來自于系列文章《瀏覽器是如何工作的系列》。感謝原作者及翻譯者,受益匪淺。
本文涉及到的關(guān)鍵點:渲染引擎工作流程、一般文檔解析方法、html解析器及其流程、樹構(gòu)造算法。

瀏覽器的主要概念

瀏覽器的主要工作的展示網(wǎng)頁資源,即請求服務(wù)器,并將結(jié)果顯示在瀏覽器窗口中,網(wǎng)頁資源的格式主要是HTML,也有PDF、image等其它各種格式,資源的定位由URL來實現(xiàn),更多請參考"網(wǎng)絡(luò)"一節(jié)。

瀏覽器結(jié)構(gòu)

1.用戶接口——包含地址欄、前進(jìn)和后退按鈕、書簽等除了顯示網(wǎng)頁以外的部分。
2.瀏覽器引擎——查詢與操作渲染引擎的接口。
3.渲染引擎——負(fù)責(zé)顯示請求的內(nèi)容,比如:如果請求的是一個HTML文檔,它負(fù)責(zé)解析HTML和CSS,把解析的內(nèi)容顯示到屏幕上。
4.網(wǎng)絡(luò)——用于網(wǎng)絡(luò)請求,如HTTP請求,它包括與平臺無關(guān)的接口和各平臺獨立的實現(xiàn)。
5.UI后端——用來繪制基礎(chǔ)元件,像組合框和窗口。它提供與平臺無關(guān)的接口,內(nèi)部使用操作系統(tǒng)的接口方法。
6.javascript解釋器——用于解釋和執(zhí)行javascript代碼。
7.數(shù)據(jù)存儲——這是一個持久層,瀏覽器需要把所有數(shù)據(jù)存儲到硬盤上,如cookies.HTML5規(guī)定了一個完整的(雖然輕量級)數(shù)據(jù)庫“web database”.
需要注意的是,與其它瀏覽器不同,chrome使用多個渲染引擎實例,每個Tab一個,每個Tab都是一個獨立進(jìn)程。

渲染引擎

一、渲染引擎工作流程

渲染引擎的功能就是渲染,在瀏覽器上顯示請求的內(nèi)容。
默認(rèn)情況下,渲染引擎可以顯示HTML和XML文檔和圖像。他也可以顯示其他類型的插件(瀏覽器擴展)。例如顯示PDF使用PDF瀏覽器插件。
渲染引擎有兩個:gecko、webkit。FF使用Gecko——“自制”Mozilla渲染引擎。 Safrai和Chrome都使用Webkit引擎。
下圖是渲染引擎的基本流程:

渲染引擎基本流程

渲染引擎開始解析HTML文檔,并且把HTML標(biāo)簽轉(zhuǎn)化為一個被叫做“內(nèi)容樹”的DOM樹,它將解析CSS樣式,包括外部樣式和內(nèi)嵌樣式。樣式數(shù)據(jù)和HTML中的顯示控制將共同用來創(chuàng)建另一棵樹——渲染樹。
渲染樹包含帶有顏色和尺寸等顯示屬性的矩形,這個矩形的順序與顯示順序一致。
渲染樹構(gòu)建完成后就是“布局”處理——確定每個節(jié)點在屏幕上的確切顯示位置。 下一個步驟就是繪制——遍歷渲染樹并用UI后端層將每一個節(jié)點繪制出來。

這是一個緩慢(漸進(jìn))的過程,為了更好的用戶體驗,渲染引擎將盡可能的把內(nèi)容顯示到屏幕上。
它不會等到所有的HTML被解析完才開始創(chuàng)建和布局渲染樹。它會在處理后續(xù)內(nèi)容的同時把已經(jīng)處理過的內(nèi)容顯示出來。

下圖是Webkit主要流程示例:


Webkit 渲染引擎基本流程

下圖是Gecko主要流程示例:


Gecko 渲染引擎基本流程

從圖2.1和圖2.2可以看出雖然Webkit和Gecko使用術(shù)語稍微不同,主要流程還是基本相同的。一個非語義上的小差別是Gecko在HTML與DOM樹之間有一個附加的層 ,稱作”content sink”,是創(chuàng)建DOM對象的工廠。

二、解析和DOM樹的建立

1、解析:

解析一個文檔,意味著把它轉(zhuǎn)換為一個有意義的結(jié)構(gòu)——代碼可以了解和使用的東西,解析 的結(jié)果通常是一個樹的節(jié)點集合,用來表示文檔結(jié)構(gòu),它被稱為解析樹或者語法樹。
例子: 解析表達(dá)式“2+3-1”,返回樹如下圖3.1

解析的結(jié)果:樹的節(jié)點集合
解析的結(jié)果:樹的節(jié)點集合

1)、語法:

解析基于文檔所遵循的語法規(guī)則——書寫所用的語言或格式——來進(jìn)行的。每一種可以解析的格式必須由確定的語法與詞匯組成。這被稱之為上下文無關(guān)語法。 人類語言并非此種語言,所以不能用常規(guī)的解析技術(shù)來解析。

2)、解析器——分析器組合:

解析器有兩個處理過程——詞法分析語法分析。
詞法分析負(fù)責(zé)把輸入切分成符號序列,符號是語言的詞匯——由該語言所有合法的單詞組成。語法分析是對該語言語法法則的應(yīng)用。
解析器通常把工作分給兩個組件——詞法分析程序(有時被叫分詞器)負(fù)責(zé)把輸入切分成合法符號序列,解析器負(fù)責(zé)按照語法規(guī)則分析文檔結(jié)構(gòu)和構(gòu)建語法樹。
詞法分析程序知道如何過濾像空格,換行之類的無關(guān)字符。如下圖3.1.2

解析器的工作流程:文檔----分詞器----語法分析器----解析樹

解析過程是迭代的。解析器通常會從詞法分析器獲取新符號并嘗試匹配句法規(guī)則。如果匹配成功,就在句法樹上創(chuàng)建相應(yīng)的節(jié)點,并繼續(xù)從詞法分析器獲取下一個符號。
如果沒有匹配的規(guī)則,解析器會內(nèi)部保存這個符號,并繼續(xù)從詞法分析器獲取符號,直到內(nèi)部保存的所有符號能夠成功匹配一個規(guī)則。
如果最終無法匹配,解析器會拋出異常。這意味著文檔無效,含有句法錯誤。

3)、轉(zhuǎn)換:

多數(shù)情況下,解析樹并非是最終結(jié)果,解析經(jīng)常被用于轉(zhuǎn)換——輸入文檔轉(zhuǎn)換為另一種格式,比如一個編譯器要把源碼編譯成機器碼,首先會解析成解析樹,然后再轉(zhuǎn)換成機器碼,如下圖:

Paste_Image.png

關(guān)于解析的具體工作示例請參照:這里 。該示例為上下文無關(guān)的語法,詞匯轉(zhuǎn)換規(guī)則用正則表達(dá)式來表示,語法規(guī)則用BNF格式定義,常規(guī)解析器只能解析上下文無關(guān)語法的語言。這種語言的一個直覺的定義是它的句法可以用BNF完整的表達(dá)。
同時介紹了有兩種基本類型——自上而下解析器和自下而上解析器各自的解析流程。

2.HTML解析器:

HTML解析器的工作是解析HTML標(biāo)記到解析樹。

1)HTML語法定義

HTML的詞匯與句法定義在w3c組織創(chuàng)建的規(guī)范中。當(dāng)前版本是HTML4,HTML5的工作正在進(jìn)行中。

2)不是上下文無關(guān)語法

在對解析器的介紹中看到,語法可以用類似BNF的格式規(guī)范地定義。不幸的是所有常規(guī)解析器的討論都不適用于HTML(我提及它們并不是為了娛樂,它們可以用于解析CSS和JavaScript)。HTML無法用解析器所需的上下文無關(guān)的語法來定義。

3)HTML DTD

HTML的定義使用DTD文件。這種格式用來定義SGML族語言,它包含對所有允許的元素的定義,包括它們的屬性和層級關(guān)系。如我們前面所說,HTML DTD構(gòu)不成上下文無關(guān)語法。 具體規(guī)則由W3C定義。

4)DOM

解析器輸出的樹是由DOM元素屬性節(jié)點組成的。DOM的全稱為:Document Object Model。它是HTML文檔的對象化描述,也是HTML元素與外界(如Javascript)的接口。
DOM與標(biāo)簽有著幾乎一一對應(yīng)的關(guān)系,如下:
<html> <body> <p>hello world</p> <div><img src="aa.png"/></div> </body> </html>
其對應(yīng)的DOM樹如下3.2.4圖:

代碼對應(yīng)的DOM結(jié)構(gòu)

與HTML一樣,DOM規(guī)范也由w3c組織制訂。參考:http://www.w3.org/DOM/DOMTR.

5)解析算法

無法使用常規(guī)的解析技術(shù),瀏覽器創(chuàng)建自定義的解析器解析HTML。解析流程如下:

HTML解析算法流程

HTML解析算法分為兩個階段:標(biāo)記化算法 + 樹構(gòu)造算法

6)標(biāo)記化算法

該算法的輸出是一個HTML標(biāo)記。該算法被表示為一個狀態(tài)機。每個狀態(tài)會消耗一個或多個字符輸入流,根據(jù)這些字符更新下一個狀態(tài)。這個決定會被當(dāng)前的標(biāo)記化狀態(tài)和樹建設(shè)狀態(tài)所影響。這意味著在下一個正確的狀態(tài)下消耗相同的字符會產(chǎn)生不同的結(jié)果。
讓我們來看看一個簡單的例子,幫助我們進(jìn)一步的理解。

基本的例子 - 標(biāo)記化下面的HTML:
`<HTML>

<BODY>  
    世界,你好  
</BODY>  

</HTML>`

初始狀態(tài)是”Data state”,當(dāng)遇到”<”時狀態(tài)改為“Tag open state”。吃掉”a-z”字符組成的符號后產(chǎn)生了”Start tag token”,狀態(tài)變更為“Tag name state”。我們一直保持此狀態(tài),直到遇到”>”。每個字符都被追加到新的符號名上。在我們的例子中,解出的符號就是”html”。
當(dāng)碰到”>”時,當(dāng)前符號完成,狀態(tài)改回“Data state”?!?lt;body>”標(biāo)簽將會以同樣的方式處理?,F(xiàn)在”html”與”body”標(biāo)簽都完成了,我們回到“Data state”狀態(tài)。吃掉”H”(”Hello world”第一個字母)時會產(chǎn)生一個字符符號,直到碰到”</body>”的”<”符號,我們就完成了一個字符符號”Hello world”。
現(xiàn)在我們回到“Tag open state”狀態(tài)。吃掉下一個輸入”/”時會產(chǎn)生一個”end tag token”并變更為“Tag name state”狀態(tài)。同樣,此狀態(tài)保持到我們碰到”>”時。這時新標(biāo)簽符號完成,我們又回到“Data state”。同樣”</html>”也會被這樣處理。具體流程如下圖:3.2.6(輸入源的分詞處理):

Paste_Image.png
7)樹構(gòu)造算法

當(dāng)解析器被創(chuàng)建時,文檔對象也被創(chuàng)建了。在樹的構(gòu)建過程中DOM樹的根節(jié)點(Documen)將被修改,元素被添加到上面去。每個分詞器完成的節(jié)點都會被樹構(gòu)建器處理。規(guī)范中定義了每一個符號與哪個DOM對象相關(guān)。除了把元素添加到DOM樹外,它還會被添加到一個開放元素堆棧。這個堆棧用于糾正嵌套錯誤和標(biāo)簽未關(guān)閉錯誤。這個算法也用狀態(tài)機描述,它的狀態(tài)叫做”insertion modes”。

讓我們看看下面輸入的樹構(gòu)建過程:

<html> <body> Hello world </body> </html>

樹的構(gòu)建過程中,輸入就是分詞過程中得到的符號序列。第一個模式叫“initial mode”。接收 html 符號后會變成“before html”模式并重新處理此模式中的符號。這會創(chuàng)建一個HTMLHtmlElement元素并追加到根文檔節(jié)點。
然后狀態(tài)改變?yōu)椤癰efore head”。我們收到”body”時,會隱式創(chuàng)建一個HTMLHeadElement,盡管我們沒有這個標(biāo)簽,它也會被創(chuàng)建并添加到樹中。
現(xiàn)在我們進(jìn)入“in head”模式,然后是“after head”,Body會被重新處理,創(chuàng)建HTMLBodyElement元素并插入,然后進(jìn)入“in body”模式。字符符號”Hello world”收到后會創(chuàng)建一個”Text”節(jié)點,所有字符都被一一追加到上面。收到body結(jié)束標(biāo)簽后進(jìn)入 “after body” 模式,收到html結(jié)束標(biāo)簽后進(jìn)入“after after body”模式。所有符號處理完后將終止解析。
如下圖(HTML樹的創(chuàng)建)

HTML樹的創(chuàng)建
8)當(dāng)解析完成后的動作

在這一階段瀏覽器會把文檔標(biāo)記為交互模式,并開始解析deferred模式的script?!眃eferred”意味著腳本應(yīng)該在文檔解析完成后執(zhí)行。腳本處理完成后將進(jìn)入”complete”狀態(tài),”load”事件發(fā)生。
HTML5規(guī)范中包含了完整的算法: http://www.w3.org/TR/html5/syntax.html#html-parser

3.CSS解析

記得在介紹中的解析概念嗎?CSS不像HTML,它是一個與上下文無關(guān)語法和能被在介紹中的解析器類型解析,其實CSS規(guī)范定義CSS的詞法和句法語法。
具體的 詞法語法 與 語法規(guī)則請點擊:這里。

webkit CSS解析器:
webkit 使用 flex 和 bison 解析器發(fā)生器從CSS 語法文件去自動創(chuàng)建解析器,在解析器介紹中,bison創(chuàng)建一個自下而上的解析器。Firefox使用自上而下的手工編寫解析器。在這兩種情況下,每一個CSS文件被解析到一個樣式表對象,每個對象都包含CSS規(guī)則。CSS規(guī)則對象包含選擇器和聲明對象和其他對象相對應(yīng)的CSS語法。如圖所示:

css解析結(jié)構(gòu)

5.處理腳本和樣式表的順序

腳本:
web模式是同步模式,作者們期望當(dāng)解析器解析到一個<script>標(biāo)簽時,腳本能被解析和立即執(zhí)行。腳本被執(zhí)行,文檔的解析暫停,如果<script>腳本是由外部引入的,必須先從網(wǎng)絡(luò)上獲取——這也是同步的,解析暫停,直到資源被獲取。這是多年來使用的模式,也被寫入到HTML4和HTML5規(guī)范中。
作者可以給<script>標(biāo)簽添加一個defer=”defer“屬性,這樣不會暫停文檔解析,文檔解析完成后,再執(zhí)行腳本。
**HTML5,增加給<script>增加了一個屬性async,可以使文檔的解析和腳本的執(zhí)行在不同的線程。 **
投機性解析:
WebKit和火狐都這樣做優(yōu)化。執(zhí)行腳本時,另一個線程解析文檔的其余部分,并找出需要從網(wǎng)絡(luò)加載的其他資源,并加載它們。在這些方式的資源可以被并行鏈接加載的整體速度是更好的。注意 - 投機解析器不修改DOM樹,節(jié)點,主分析器,它僅僅解析外部腳本,樣式表和圖片等外部資源的引用。
樣式表:
樣式表在另一方面有不同的模式,從概念上講,它似乎是因為樣式表沒有改變的DOM樹,沒有任何理由等待和停止解析文檔。有一個問題,當(dāng)文檔解析時腳本訪問樣式信息,如果樣式么有加載和解析,腳本將會得到錯誤的回答以及會引起一系列問題。這看起來像一種邊緣情況,但是相當(dāng)普遍,
火狐中有一個樣式表一直在加載和解析時,會阻止所有腳本。Webkit的塊的腳本,只有當(dāng)他們試圖訪問某些可能卸載樣式表的樣式屬性影響時,才會阻止所有腳本。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容