用戶在使用瀏覽器訪問一個(gè)網(wǎng)站時(shí)需要先通過HTTP(HTTPS)協(xié)議向服務(wù)器發(fā)送請(qǐng)求,之后服務(wù)器返回HTML文件與響應(yīng)信息。這時(shí),瀏覽器會(huì)根據(jù)HTML文件來(lái)進(jìn)行解析與渲染(該階段還包括向服務(wù)器請(qǐng)求非內(nèi)聯(lián)的CSS文件與JavaScript文件或者其他資源),最終再將頁(yè)面呈現(xiàn)在用戶面前。
關(guān)鍵渲染路徑
覽器接收到服務(wù)器返回的HTML、CSS和JavaScript字節(jié)數(shù)據(jù)并對(duì)其進(jìn)行解析和轉(zhuǎn)變成像素的渲染過程被稱為關(guān)鍵渲染路徑
瀏覽器在渲染頁(yè)面前需要先構(gòu)建出DOM樹與CSSOM樹(如果沒有DOM樹和CSSOM樹就無(wú)法確定頁(yè)面的結(jié)構(gòu)與樣式,所以這兩項(xiàng)是必須先構(gòu)建出來(lái)的)
構(gòu)建DOM樹
DOM樹全稱為Document Object Model文檔對(duì)象模型,它是HTML和XML文檔的編程接口,提供了對(duì)文檔的結(jié)構(gòu)化表示,并定義了一種可以使程序?qū)υ摻Y(jié)構(gòu)進(jìn)行訪問的方式(比如JavaScript就是通過DOM來(lái)操作結(jié)構(gòu)、樣式和內(nèi)容)。DOM將文檔解析為一個(gè)由節(jié)點(diǎn)和對(duì)象組成的集合,可以說一個(gè)WEB頁(yè)面其實(shí)就是一個(gè)DOM。
瀏覽器從網(wǎng)絡(luò)或硬盤中獲得HTML字節(jié)數(shù)據(jù)后會(huì)經(jīng)過一個(gè)流程將字節(jié)解析為DOM樹: 字節(jié) -> 字符 -> 令牌 -> 節(jié)點(diǎn)對(duì)象 -> 對(duì)象模型
下面通過一個(gè)例子來(lái)清晰地展示整個(gè)過程
<head>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="style.css" rel="stylesheet">
<title>Critical Path</title>
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg"></div>
</body>
</html>

構(gòu)建CSSOM樹
CSSOM樹全稱為Cascading Style Sheets Object Model層疊樣式表對(duì)象模型,它與DOM樹的含義相差不大,只不過它是CSS的對(duì)象集合.
當(dāng)在構(gòu)建DOM樹的過程中, 如果遇到<link>標(biāo)簽, 瀏覽器會(huì)發(fā)送請(qǐng)求獲得該標(biāo)簽中標(biāo)記的CSS文件(使用內(nèi)聯(lián)CSS可以省略請(qǐng)求的步驟提高速度,但沒有必要為了這點(diǎn)速度而丟失了模塊化與可維護(hù)性, style.css中的內(nèi)容如下:
body { font-size: 16px }
p { font-weight: bold }
span { color: red }
p span { display: none }
img { float: right }
瀏覽器獲得外部CSS文件的數(shù)據(jù)后,就會(huì)像構(gòu)建DOM樹一樣開始構(gòu)建CSSOM樹.

構(gòu)建渲染樹
在構(gòu)建了DOM樹和CSSOM樹之后,瀏覽器只是擁有了兩個(gè)互相獨(dú)立的對(duì)象集合,DOM樹描述了文檔的結(jié)構(gòu)與內(nèi)容,CSSOM樹則描述了對(duì)文檔應(yīng)用的樣式規(guī)則,想要渲染出頁(yè)面,就需要將DOM樹與CSSOM樹結(jié)合在一起,這就是渲染樹.

瀏覽器會(huì)先從DOM樹的根節(jié)點(diǎn)開始遍歷每個(gè)可見節(jié)點(diǎn)(不可見的節(jié)點(diǎn)自然就沒必要渲染到頁(yè)面了,不可見的節(jié)點(diǎn)還包括被CSS設(shè)置了
display: none屬性的節(jié)點(diǎn),值得注意的是visibility: hidden屬性并不算是不可見屬性,它的語(yǔ)義是隱藏元素,但元素仍然占據(jù)著布局空間,所以它會(huì)被渲染成一個(gè)空框)對(duì)每個(gè)可見節(jié)點(diǎn),找到其適配的CSS樣式規(guī)則并應(yīng)用
渲染樹構(gòu)建完成,每個(gè)節(jié)點(diǎn)都是可見節(jié)點(diǎn)并且都含有其內(nèi)容和對(duì)應(yīng)規(guī)則的樣式
布局
布局階段工作則需要計(jì)算每個(gè)節(jié)點(diǎn)在窗口內(nèi)的確切位置與大小.
CSS采用了一種叫做盒子模型的思維模型來(lái)表示每個(gè)節(jié)點(diǎn)與其他元素之間的距離,盒子模型包括外邊距(Margin),內(nèi)邊距(Padding),邊框(Border),內(nèi)容(Content)。頁(yè)面中的每個(gè)標(biāo)簽其實(shí)都是一個(gè)個(gè)盒子.
布局階段會(huì)從渲染樹的根節(jié)點(diǎn)開始遍歷,然后確定每個(gè)節(jié)點(diǎn)對(duì)象在頁(yè)面上的確切大小與位置,布局階段的輸出是一個(gè)盒子模型,它會(huì)精確地捕獲每個(gè)元素在屏幕內(nèi)的確切位置與大小,所有相對(duì)的測(cè)量值也都會(huì)被轉(zhuǎn)換為屏幕內(nèi)的絕對(duì)像素值.
繪制
當(dāng)Layout布局事件完成后,瀏覽器會(huì)立即發(fā)出Paint Setup與Paint事件,開始將渲染樹繪制成像素,繪制所需的時(shí)間跟CSS樣式的復(fù)雜度成正比,繪制完成后,用戶就可以看到頁(yè)面的最終呈現(xiàn)效果了.
總結(jié)
- 解析html標(biāo)簽, 構(gòu)建DOM樹
- 解析css標(biāo)簽, 構(gòu)建CSSOM樹
- 把DOM和CSSOM組合成 渲染樹(render tree)
- 在渲染樹基礎(chǔ)上進(jìn)行布局, 計(jì)算每個(gè)節(jié)點(diǎn)的幾何結(jié)構(gòu)
- 把每個(gè)節(jié)點(diǎn)繪制到屏幕上
值得注意的是,這個(gè)過程是逐步完成的,為了更好的用戶體驗(yàn),渲染引擎將會(huì)盡可能早的將內(nèi)容呈現(xiàn)到屏幕上,并不會(huì)等到所有的html都解析完成之后再去構(gòu)建和布局render樹。它是解析完一部分內(nèi)容就顯示一部分內(nèi)容,同時(shí),可能還在通過網(wǎng)絡(luò)下載其余內(nèi)容.