新聞中心
大家好,我是 CUGGZ。

發(fā)展壯大離不開廣大客戶長(zhǎng)期以來的信賴與支持,我們將始終秉承“誠(chéng)信為本、服務(wù)至上”的服務(wù)理念,堅(jiān)持“二合一”的優(yōu)良服務(wù)模式,真誠(chéng)服務(wù)每家企業(yè),認(rèn)真做好每個(gè)細(xì)節(jié),不斷完善自我,成就企業(yè),實(shí)現(xiàn)共贏。行業(yè)涉及石牌坊等,在網(wǎng)站建設(shè)公司、營(yíng)銷型網(wǎng)站、WAP手機(jī)網(wǎng)站、VI設(shè)計(jì)、軟件開發(fā)等項(xiàng)目上具有豐富的設(shè)計(jì)經(jīng)驗(yàn)。
今天來分享一下瀏覽器的渲染原理及流程。
前言
先來看看 Chrome 瀏覽器的多進(jìn)程架構(gòu):
通常,我們打包出來的 HTML、CSS、JavaScript 等文件,經(jīng)過瀏覽器運(yùn)行之后就會(huì)顯示出頁(yè)面,這個(gè)過程就是瀏覽器的渲染進(jìn)程來操作實(shí)現(xiàn)的,渲染進(jìn)程的主要任務(wù)就是將靜態(tài)資源轉(zhuǎn)化為可視化界面:
對(duì)于中間的瀏覽器,它就是一個(gè)黑盒,下面就來看看這個(gè)黑盒是如何將靜態(tài)資源轉(zhuǎn)化為前端界面的。由于渲染機(jī)制比較復(fù)雜,所以渲染模塊在執(zhí)行過程中會(huì)被劃分為很多子階段,輸入的靜態(tài)資源經(jīng)過這些子階段,最后輸出頁(yè)面。我們將一個(gè)處理流程稱為渲染流水線,其大致流程如下圖所示:
這里主要包含五個(gè)過程:
- DOM樹構(gòu)建:渲染引擎使用HTML解析器(調(diào)用XML解析器)解析HTML文檔,將各個(gè)HTML元素逐個(gè)轉(zhuǎn)化成DOM節(jié)點(diǎn),從而生成DOM樹。
- CSSOM樹構(gòu)建:CSS解析器解析CSS,并將其轉(zhuǎn)化為CSS對(duì)象,將這些CSS對(duì)象組裝起來,構(gòu)建CSSOM樹。
- 渲染樹構(gòu)建:DOM 樹和 CSSOM 樹都構(gòu)建完成以后,瀏覽器會(huì)根據(jù)這兩棵樹構(gòu)建出一棵渲染樹。
- 頁(yè)面布局:渲染樹構(gòu)建完畢之后,元素的位置關(guān)系以及需要應(yīng)用的樣式就確定了,這時(shí)瀏覽器會(huì)計(jì)算出所有元素的大小和絕對(duì)位置。
- 頁(yè)面繪制:頁(yè)面布局完成之后,瀏覽器會(huì)將根據(jù)處理出來的結(jié)果,把每一個(gè)頁(yè)面圖層轉(zhuǎn)換為像素,并對(duì)所有的媒體文件進(jìn)行解碼。
對(duì)于這五個(gè)流程,每一階段都有對(duì)應(yīng)的產(chǎn)物:DOM樹、CSSOM樹、渲染樹、盒模型、界面。
下圖為渲染引擎工作流程中各個(gè)步驟所對(duì)應(yīng)的模塊:
從圖中可以看出,渲染引擎主要包含的模塊有:
- HTML 解析器:解析HTML文檔,主要作用是將HTML文檔轉(zhuǎn)換成DOM樹。
- CSS 解析器:將DOM中的各個(gè)元素對(duì)象進(jìn)行計(jì)算,獲取樣式信息,用于渲染樹的構(gòu)建。
- JavaScript 解釋器:使用JavaScript可以修改網(wǎng)頁(yè)的內(nèi)容、CSS規(guī)則等。JavaScript解釋器能夠解釋JavaScript代碼,并通過DOM接口和CSSOM接口來修改網(wǎng)頁(yè)內(nèi)容、樣式規(guī)則,從而改變渲染結(jié)果。
- 頁(yè)面布局:DOM創(chuàng)建之后,渲染引擎將其中的元素對(duì)象與樣式規(guī)則進(jìn)行結(jié)合,可以得到渲染樹。布局則是針對(duì)渲染樹,計(jì)算其各個(gè)元素的大小、位置等布局信息。
- 頁(yè)面繪制:使用圖形庫(kù)將布局計(jì)算后的渲染樹繪制成可視化的圖像結(jié)果。
DOM樹構(gòu)建
在說構(gòu)建DOM樹之前,我們需要知道,為什么要構(gòu)建DOM樹呢? 這是因?yàn)?,瀏覽器無法直接理解和使用 HTML,所以需要將HTML轉(zhuǎn)化為瀏覽器能夠理解的結(jié)構(gòu)——DOM樹。
了解過數(shù)據(jù)結(jié)構(gòu)的小伙伴對(duì)于樹結(jié)構(gòu)應(yīng)該不陌生,樹是由結(jié)點(diǎn)或頂點(diǎn)和邊組成的且不存在著任何環(huán)的一種數(shù)據(jù)結(jié)構(gòu)。一棵非空的樹包括一個(gè)根結(jié)點(diǎn),還有多個(gè)附加結(jié)點(diǎn),所有結(jié)點(diǎn)構(gòu)成一個(gè)多級(jí)分層結(jié)構(gòu)。下面通過一張圖來看看什么是樹結(jié)構(gòu):
對(duì)于上面的三個(gè)結(jié)構(gòu),前兩個(gè)都是樹,他們都只有唯一的根節(jié)點(diǎn),而且不存在環(huán)結(jié)構(gòu)。而第三個(gè)存在環(huán),所以就不是一個(gè)樹結(jié)構(gòu)。
在頁(yè)面中,每個(gè)HTML標(biāo)簽都會(huì)被瀏覽器解析成文檔對(duì)象。HTML本質(zhì)上就是一個(gè)嵌套結(jié)構(gòu),在解析時(shí)會(huì)把每個(gè)文檔對(duì)象用一個(gè)樹形結(jié)構(gòu)組織起來,所有的文檔對(duì)象都會(huì)掛在document上,這種組織方式就是HTML最基礎(chǔ)的結(jié)構(gòu)——文檔對(duì)象模型(DOM),這棵樹的每個(gè)文檔對(duì)象就叫做DOM節(jié)點(diǎn)。
在渲染引擎中,DOM 有三個(gè)層面的作用:
- 從頁(yè)面的視角來看,DOM 是生成頁(yè)面的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)。
- 從 JavaScript 腳本視角來看,DOM 提供給 JavaScript 腳本操作的接口,通過這套接口,JavaScript 可以對(duì) DOM 結(jié)構(gòu)進(jìn)行訪問,從而改變文檔的結(jié)構(gòu)、樣式和內(nèi)容。
- 從安全視角來看,DOM 是一道安全防護(hù)線,一些不安全的內(nèi)容在 DOM 解析階段會(huì)被拒之門外。
在渲染引擎內(nèi)部,HTML 解析器負(fù)責(zé)將 HTML 字節(jié)流轉(zhuǎn)換為 DOM 結(jié)構(gòu),其轉(zhuǎn)化過程如下:
1、字符流 → 詞(token)
HTML結(jié)構(gòu)會(huì)首先通過分詞器將字節(jié)流拆分為詞(token)。Token分為Tag Token 和文本 Token。下面來看一個(gè)HTML代碼是如何被拆分的:
hello world
對(duì)于這段代碼,可以拆成詞:
可以看到,Tag Token 又分 StartTag 和 EndTag,
、?就是 StartTag ,、
這里會(huì)通過狀態(tài)機(jī)將字符拆分成token,所謂的狀態(tài)機(jī)就是將每個(gè)詞的特征逐個(gè)拆分成獨(dú)立的狀態(tài),然后再將所有詞的特征字符合并起來,形成一個(gè)連通的圖結(jié)構(gòu)。那為什么要使用狀態(tài)機(jī)呢?因?yàn)槊孔x取一個(gè)字符,都要做一次決策,這些決策都和當(dāng)前的狀態(tài)有關(guān)。
實(shí)際上,狀態(tài)機(jī)的作用就是用來做詞法分析的,將字符流分解為詞(token)。
2、詞(token)→ DOM樹
接下來就需要將 Token 解析為 DOM 節(jié)點(diǎn),并將 DOM 節(jié)點(diǎn)添加到 DOM 樹中。這個(gè)過程是通過棧結(jié)構(gòu)來實(shí)現(xiàn)的,這個(gè)棧主要用來計(jì)算節(jié)點(diǎn)之間的父子關(guān)系,上面步驟中生成的token會(huì)按順序壓入棧中,該過程的規(guī)則如下:
- 如果分詞器解析出來是StartTag Token,HTML 解析器會(huì)為該 Token 創(chuàng)建一個(gè) DOM 節(jié)點(diǎn),然后將該節(jié)點(diǎn)加入到 DOM 樹中,它的父節(jié)點(diǎn)就是棧中相鄰的那個(gè)元素生成的節(jié)點(diǎn)。
- 如果分詞器解析出來是文本Token,那么會(huì)生成一個(gè)文本節(jié)點(diǎn),然后將該節(jié)點(diǎn)加入到 DOM 樹中,文本 Token 是不需要壓入到棧中,它的父節(jié)點(diǎn)就是當(dāng)前棧頂 Token 所對(duì)應(yīng)的 DOM 節(jié)點(diǎn)。
- 如果分詞器解析出來的是EndTag Token,比如是 EndTag div,HTML 解析器會(huì)查看 Token 棧頂?shù)脑厥欠袷?StarTag div,如果是,就將 StartTag div從棧中彈出,表示該 div 元素解析完成。
通過分詞器產(chǎn)生的新 Token 就這樣不停地入棧和出棧,整個(gè)解析過程就這樣一直持續(xù)下去,直到分詞器將所有字節(jié)流分詞完成。
下面來看看這的Token棧是如何工作的,有如下HTML結(jié)構(gòu):
hello juejin
hello world
開始時(shí),HTML解析器會(huì)創(chuàng)建一個(gè)根為 document 的空的 DOM 結(jié)構(gòu),同時(shí)將 StartTag document 的Token壓入棧中,然后再將解析出來的第一個(gè) StartTag html 壓入棧中,并創(chuàng)建一個(gè) html 的DOM節(jié)點(diǎn),添加到 document 上,這時(shí) Token 棧和 DOM樹 如下:
接下來body和div標(biāo)簽也會(huì)和上面的過程一樣,進(jìn)行入棧操作:
隨后就會(huì)解析到 div標(biāo)簽中的文本Token,渲染引擎會(huì)為該 Token 創(chuàng)建一個(gè)文本節(jié)點(diǎn),并將該 Token 添加到 DOM 中,它的父節(jié)點(diǎn)就是當(dāng)前 Token 棧頂元素對(duì)應(yīng)的節(jié)點(diǎn):
接下來就是第一個(gè)EndTag div,這時(shí) HTML 解析器會(huì)判斷當(dāng)前棧頂元素是否是 StartTag div,如果是,則從棧頂彈出 StartTag div,如下圖所示:
再之后的過程就和上面類似了,最終的結(jié)果如下:
CSSOM樹構(gòu)建
上面已經(jīng)基本了解了DOM的構(gòu)建過程,但是這個(gè)DOM結(jié)構(gòu)只包含節(jié)點(diǎn),并不包含任何的樣式信息。下面就來看看,瀏覽器是如何把CSS樣式應(yīng)用到DOM節(jié)點(diǎn)上的。
同樣,瀏覽器也是無法直接理解CSS代碼的,需要將其瀏覽器可以理解的CSSOM樹。實(shí)際上。瀏覽器在構(gòu)建 DOM 樹的同時(shí),如果樣式也加載完成了,那么 CSSOM 樹也會(huì)同步構(gòu)建。CSSOM 樹和 DOM 樹類似,它主要有兩個(gè)作用:
- 提供給 JavaScript 操作樣式的能力。
- 為渲染樹的合成提供基礎(chǔ)的樣式信息。
不過,CSSOM 樹和 DOM 樹是獨(dú)立的兩個(gè)數(shù)據(jù)結(jié)構(gòu),它們并沒有一一對(duì)應(yīng)關(guān)系。DOM 樹描述的是 HTML 標(biāo)簽的層級(jí)關(guān)系,CSSOM 樹描述的是選擇器之間的層級(jí)關(guān)系??梢栽跒g覽器的控制臺(tái),通過document.styleSheets命令來查看CSSOM樹:
那CSS樣式的來源有哪些呢?
CSS樣式的來源主要有三種:
- 通過 link 引用的外部 CSS 樣式文件。
CUGGZ
hello world


咨詢
建站咨詢