瀏覽器的工作原理是非常重要的基礎(chǔ)知識(shí),而且內(nèi)容非常多,有一篇文章How browsers work非常著名缰猴,其內(nèi)容也十分詳實(shí)氛琢,有中文版本前端必讀:瀏覽器內(nèi)部工作原理喊递。在此基礎(chǔ)上,我將一些要點(diǎn)用我自己能理解的話總結(jié)一遍阳似,一是梳理一遍整個(gè)的過(guò)程骚勘,另外是方便今后的復(fù)習(xí)。
總論
瀏覽器的主要構(gòu)成
瀏覽器的主要構(gòu)成是用戶界面撮奏、瀏覽器引擎俏讹、渲染引擎、網(wǎng)絡(luò)畜吊、UI后端泽疆、JS解釋器、數(shù)據(jù)存儲(chǔ)玲献。在chorme中殉疼,每個(gè)標(biāo)簽頁(yè)都是獨(dú)立的進(jìn)程(意味著單個(gè)標(biāo)簽頁(yè)的崩潰不會(huì)影響到其他標(biāo)簽頁(yè))
渲染引擎
渲染引擎
面試常見的一個(gè)問題是瀏覽器的內(nèi)核有哪些,瀏覽器的內(nèi)核包括渲染引擎和JS引擎捌年,但隨著JS引擎越來(lái)越獨(dú)立瓢娜,內(nèi)核就傾向只指代渲染引擎了。
瀏覽器內(nèi)核 | 常見瀏覽器 |
---|---|
Trident內(nèi)核 | IE礼预、傲游眠砾、世界之窗瀏覽器、Avant托酸、騰訊TT褒颈、360、Netscape 8励堡、NetCaptor谷丸、Sleipnir、GOSURF念秧、GreenBrowser和KKman等 |
Gecko內(nèi)核 | Firefox淤井、Netscape6至9 |
WebKit內(nèi)核 | Safari、chrome、Opera |
EdgeHTML內(nèi)核 | Edge |
渲染的主流程
解析html以構(gòu)建dom樹 -> 構(gòu)建render樹 -> 布局render樹 -> 繪制render樹
- 渲染引擎解析html币狠,構(gòu)建“DOM tree”
- CSS文件及style標(biāo)簽中的樣式信息以及html中的可見性指令構(gòu)建CSSOM
- DOM tree和CSSOM一起構(gòu)建Render樹
- Render樹是一些含有各種屬性的矩形游两,根據(jù)某種規(guī)則顯示在屏幕上。
- 構(gòu)建了Render樹后漩绵,會(huì)布局Render樹贱案,這一過(guò)程確定每個(gè)節(jié)點(diǎn)的坐標(biāo)
- 布局之后是繪制,遍歷Render樹止吐,并使用UI后端層繪制
-
webkit主流程
-
Geoko主流程
- Geoko中的Frame樹就是Render樹宝踪,Reflow就是Layout,僅是名字不同
- Geoko在解析HTMl和構(gòu)建DOM樹之間有一層Content Sink(內(nèi)容接收器)碍扔,用于生成DOM元素瘩燥,這是webkit所沒有的、
解析與DOM樹構(gòu)建
幾個(gè)概念:
- 解析:就是將一個(gè)文檔的結(jié)構(gòu)轉(zhuǎn)換為代碼可以理解和使用的結(jié)構(gòu)(通常是文檔結(jié)構(gòu)的節(jié)點(diǎn)樹)不同,稱為解析樹或語(yǔ)法樹厉膀。
- 文法:解析要基于文法,文法由詞匯和語(yǔ)法規(guī)則組成二拐,此處的文法是上下文無(wú)關(guān)文法(“DTD”不是上下文無(wú)關(guān)服鹅,因此后面介紹的解析方法不適合HTML,但可用于CSS和JS)百新。
解析的過(guò)程(由淺入深)
- 解析的兩個(gè)子過(guò)程——語(yǔ)法分析及詞法分析
-
詞法分析:將輸入分解為符號(hào)企软。
- 符號(hào):是語(yǔ)言的詞匯表——基本有效單元的集合。
- 詞法分析器將輸入分解為合法的符號(hào)
-
語(yǔ)法分析:指對(duì)語(yǔ)言應(yīng)用語(yǔ)法規(guī)則饭望。
- 解析器:根據(jù)語(yǔ)言的語(yǔ)法規(guī)則分析文檔結(jié)構(gòu)仗哨,從而構(gòu)建解析樹
-
詞法分析:將輸入分解為符號(hào)企软。
- 解析的過(guò)程進(jìn)一步可視為四步:源文檔 -> 詞法分析 -> 語(yǔ)法分析 -> 解析樹
- 解析的更細(xì)致的迭代過(guò)程是:
- 詞法分析器得到符號(hào),傳給解析器
- 解析器用符號(hào)匹配語(yǔ)法規(guī)則
* 若匹配上規(guī)則铅辞,則符號(hào)對(duì)應(yīng)的節(jié)點(diǎn)將被添加到解析樹上 * 若沒有匹配上規(guī)則藻治,解析器將在內(nèi)部保存該符號(hào),然后從詞法分析器取下一個(gè)符號(hào)巷挥,再次匹配規(guī)則 * 若在之后能使內(nèi)部符號(hào)匹配上規(guī)則,則符號(hào)對(duì)應(yīng)的節(jié)點(diǎn)將被添加到解析樹上 * 若到最后都沒有匹配上規(guī)則验靡,解析器將拋出一個(gè)異常倍宾,這意味著文檔無(wú)效或是包含語(yǔ)法錯(cuò)誤。
- 最終得到解析樹或異常
- 解析一般在轉(zhuǎn)換(將輸入文檔轉(zhuǎn)換為另一種格式)中使用胜嗓,因此高职,解析樹可能不是最終結(jié)果,比如編譯辞州。其過(guò)程為:源碼 -> 解析 -> 解析樹 -> 轉(zhuǎn)換 -> 機(jī)器碼
解析器類型
- 自頂向下解析怔锌,查看語(yǔ)法的最高層結(jié)構(gòu)并試著匹配其中一個(gè)
- 自底向上解析,從輸入開始,逐步將其轉(zhuǎn)換為語(yǔ)法規(guī)則埃元,從底層規(guī)則開始直到匹配高層規(guī)則
- 前文有說(shuō)過(guò)涝涤,之前提到的都是上下文無(wú)關(guān)文法,但HTML的格式定義--“DTD”不是上下文無(wú)關(guān)岛杀,所以傳統(tǒng)解析方式(自頂向下或自底向上)都不適用于HTML(可用于CSS和JS)
DOM
- 輸出的樹阔拳,也就是解析樹,是由DOM元素節(jié)點(diǎn)(包括文本節(jié)點(diǎn)和屬性節(jié)點(diǎn))組成的类嗤。
- 樹的根是“document”對(duì)象
- DOM和標(biāo)簽基本是一一對(duì)應(yīng)的關(guān)系
HTML解析
- HTML解析包括兩個(gè)階段——符號(hào)化(tokeniser)及構(gòu)建樹(tree construction)(和之前的具體算法不一樣糊肠,但幾個(gè)過(guò)程是類似的)
- 符號(hào)化(可和傳統(tǒng)的詞法分析類比):是詞法分析的過(guò)程,符號(hào)識(shí)別器將輸入解析為符號(hào)(html的符號(hào)包括開始標(biāo)簽遗锣、結(jié)束標(biāo)簽货裹、屬性名及屬性值)。將其傳遞給樹構(gòu)建器精偿。
- 構(gòu)建樹(可和傳統(tǒng)的語(yǔ)法分析類比):樹構(gòu)造器處理傳來(lái)的符號(hào)弧圆,根據(jù)規(guī)范每個(gè)符號(hào)會(huì)創(chuàng)建對(duì)應(yīng)的Dom元素,這些元素除了會(huì)被添加到Dom樹上还最,還將被添加到開放元素堆棧中墓阀。這個(gè)堆棧用來(lái)糾正嵌套的未匹配和未閉合標(biāo)簽(HTML“寬容”的原因)。
-
HTML解析流程
解析結(jié)束時(shí)的處理
- 在此階段拓轻,瀏覽器會(huì)將文檔標(biāo)注為交互狀態(tài)(interactive)
- 開始解析那些處于“deferred”模式的腳本斯撮,也就是那些應(yīng)在文檔解析完成后才執(zhí)行的腳本
- 然后,文檔狀態(tài)將設(shè)置為“完成”(complete)扶叉,一個(gè)“加載”(load)事件將隨之觸發(fā)
CSS解析
- css屬于上下文無(wú)關(guān)文法勿锅,可以用前面所描述的解析器(自頂向下或自底向上)來(lái)解析
- Webkit的CSS解析器將每個(gè)css文件解析為樣式表對(duì)象,每個(gè)對(duì)象包含css規(guī)則枣氧,css規(guī)則對(duì)象包含選擇器和聲明對(duì)象溢十,以及其他一些符合css語(yǔ)法的對(duì)象
處理腳本及樣式表的順序
腳本
- 解析到一個(gè)script標(biāo)簽時(shí)立即解析執(zhí)行腳本,并阻塞文檔的解析直到腳本執(zhí)行完达吞。
- 如果腳本是外引的张弛,則網(wǎng)絡(luò)必須先請(qǐng)求到這個(gè)資源——這個(gè)過(guò)程也是同步的,會(huì)阻塞文檔的解析直到資源被請(qǐng)求到酪劫。
- 開發(fā)者可以將腳本標(biāo)識(shí)為defer吞鸭,以使其不阻塞文檔解析,并在文檔解析結(jié)束后執(zhí)行(
<script defer src="script.js"></script>
)覆糟。Html5增加了標(biāo)記腳本為異步的選項(xiàng)刻剥,以使腳本的解析執(zhí)行使用另一個(gè)線程。(<script async src="script.js"></script>
)
預(yù)解析
- 當(dāng)執(zhí)行腳本時(shí)滩字,另一個(gè)線程解析剩下的文檔造虏,并加載后面需要通過(guò)網(wǎng)絡(luò)加載的資源御吞。這種方式可以使資源并行加載從而使整體速度更快
- 需要注意的是,預(yù)解析并不改變Dom樹漓藕,它將這個(gè)工作留給主解析過(guò)程陶珠,自己只解析外部資源的引用,比如外部腳本撵术、樣式表及圖片
樣式表
- 因?yàn)閳?zhí)行腳本可能會(huì)請(qǐng)求樣式信息背率,如果此時(shí)樣式還沒有被加載和解析完成,那么腳本可能出錯(cuò)嫩与。所以樣式表會(huì)阻塞腳本執(zhí)行(不會(huì)阻塞外部腳本的加載)
- Firefox在存在樣式表還在加載和解析時(shí)阻塞所有的腳本
- Chrome只在當(dāng)腳本試圖訪問某些可能被未加載的樣式表所影響的特定的樣式屬性時(shí)才阻塞這些腳本
渲染樹的構(gòu)建
- DOM樹構(gòu)建完后寝姿,開始構(gòu)建渲染樹。Firefox將渲染樹中的元素稱為frames划滋,WebKit則用renderer或渲染對(duì)象來(lái)描述這些元素饵筑。
- 每個(gè)渲染對(duì)象用一個(gè)和該節(jié)點(diǎn)的css盒模型相對(duì)應(yīng)的矩形區(qū)域來(lái)表示,包含諸如寬处坪、高和位置之類的幾何信息
渲染樹和Dom樹的關(guān)系
- 不可見的Dom元素(比如
<head>
)不會(huì)被插入渲染樹根资,display屬性為none的元素也不會(huì)在渲染樹中出現(xiàn) - 當(dāng)文本因?yàn)閷挾炔粔蚨坌袝r(shí),新行將作為額外的渲染元素被添加
- 一個(gè)行內(nèi)元素只能僅包含行內(nèi)元素或僅包含塊狀元素同窘,在存在混合內(nèi)容時(shí)玄帕,將會(huì)創(chuàng)建匿名的塊狀渲染對(duì)象包裹住行內(nèi)元素。
創(chuàng)建樹的流程
- Firefox用一個(gè)監(jiān)聽器監(jiān)聽DOM想邦,F(xiàn)rame Constructor計(jì)算樣式并創(chuàng)建Frame
- Webkit中每個(gè)Dom節(jié)點(diǎn)有一個(gè)attach方法裤纹,調(diào)用新節(jié)點(diǎn)的attach方法將節(jié)點(diǎn)插入到Dom樹中
樣式計(jì)算
- 創(chuàng)建渲染樹需要計(jì)算出每個(gè)渲染對(duì)象的可視屬性,這可以通過(guò)計(jì)算每個(gè)元素的樣式屬性得到
- 樣式包括各種來(lái)源的樣式表丧没,行內(nèi)樣式元素及html中的可視化屬性(例如bgcolor)鹰椒,可視化屬性轉(zhuǎn)化為css樣式屬性。
樣式表的級(jí)聯(lián)順序
具有同等級(jí)別的聲明將根據(jù)specifity以及它們被定義時(shí)的順序進(jìn)行排序呕童。
- 瀏覽器聲明
- 用戶聲明
- 作者的一般聲明
- 作者的important聲明
- 用戶important聲明
Specifity
- 如果聲明來(lái)自style屬性漆际,而不是一個(gè)選擇器的規(guī)則,則計(jì)1夺饲,否則計(jì)0(=a)
- 計(jì)算選擇器中id屬性的數(shù)量(=b)
- 計(jì)算選擇器中class及偽類的數(shù)量(=c)
- 計(jì)算選擇器中元素名及偽元素的數(shù)量(=d)
連接a-b-c-d四個(gè)數(shù)量將得到specifity奸汇。四級(jí)(a、b往声、c茫蛹、d)之間并不是簡(jiǎn)單的相加關(guān)系。同一級(jí)(例如:a對(duì)a)的才具有可比關(guān)系
布局
當(dāng)渲染對(duì)象被創(chuàng)建并添加到樹中烁挟,它們并沒有位置和大小,計(jì)算這些值的過(guò)程稱為layout或reflow骨坑。
繪制
- 遍歷渲染樹并調(diào)用渲染對(duì)象的paint方法將它們的內(nèi)容顯示在屏幕上撼嗓,繪制使用UI基礎(chǔ)組件
- 一個(gè)塊渲染對(duì)象的堆棧順序是:
1. 背景色
2. 背景圖
3. border
4. children
5. outline
渲染引擎的線程
- 渲染引擎是單線程的柬采,除了網(wǎng)絡(luò)操作以外,幾乎所有的事情都在單一的線程中處理且警,在Firefox和Safari中粉捻,這是瀏覽器的主線程,Chrome中這是tab的主線程斑芜。
- 網(wǎng)絡(luò)操作由幾個(gè)并行線程執(zhí)行肩刃,并行連接的個(gè)數(shù)是受限的(通常是2-6個(gè))。
事件循環(huán)
瀏覽器主線程是一個(gè)事件循環(huán)杏头,它被設(shè)計(jì)為無(wú)限循環(huán)以保持執(zhí)行過(guò)程的可用盈包,等待事件(例如layout和paint事件)并執(zhí)行它們