瀏覽器的渲染機制及優(yōu)化

用戶在使用瀏覽器訪問一個網(wǎng)站時需要先通過HTTP協(xié)議向服務(wù)器發(fā)送請求潭千,之后服務(wù)器返回HTML文件與響應(yīng)信息。這時柏卤,瀏覽器會根據(jù)HTML文件來進(jìn)行解析與渲染(該階段還包括向服務(wù)器請求非內(nèi)聯(lián)的CSS文件與JavaScript文件或者其他資源),最終再將頁面呈現(xiàn)在用戶面前。

其中網(wǎng)頁的渲染都是由瀏覽器完成的忿墅,那么如果一個網(wǎng)站的頁面加載速度太慢會導(dǎo)致用戶體驗不夠友好,通過詳解瀏覽器渲染頁面的過程來引入一些基本的瀏覽器性能優(yōu)化方案沮峡,讓瀏覽器更快地渲染你的網(wǎng)頁并快速響應(yīng)從而提高用戶體驗疚脐。

關(guān)鍵渲染路徑

瀏覽器接收到服務(wù)器返回的HTMLCSSJavascript字節(jié)數(shù)據(jù)并對其進(jìn)行解析和轉(zhuǎn)變成像素的渲染過程稱為關(guān)鍵渲染路徑邢疙。包括以下步驟:
  1. 解析HTML棍弄,生成DOM樹(DOM)
  2. 解析CSS,生成CSSOM樹(CSSOM)
  3. 將DOM和CSSOM合并疟游,生成渲染樹(Render-Tree)
  4. 計算渲染樹的布局(Layout)
  5. 將布局渲染到屏幕上(Paint)

通過優(yōu)化關(guān)鍵渲染路徑即可以縮短瀏覽器渲染頁面的時間呼畸。

構(gòu)建DOM樹與CSSOM樹

瀏覽器在渲染頁面前需要先構(gòu)建出DOM樹和CSSOM(如果沒有DOM樹和CSSOM樹就無法確定頁面的結(jié)構(gòu)與樣式,所以這兩項是必須先構(gòu)建出來的)颁虐。

DOM

全稱為Document Object Model文檔對象模型蛮原,它是HTMLXML文檔的編程接口,提供了對文檔的結(jié)構(gòu)化表示另绩,并定義了一種可以使程序?qū)υ摻Y(jié)構(gòu)進(jìn)行訪問的方式儒陨。

比如JavaScript就是通過DOM來操作結(jié)構(gòu)花嘶、樣式和內(nèi)容。DOM將文檔解析為一個由節(jié)點和對象組成的集合蹦漠,可以說一個WEB頁面其實就是一個DOM椭员。

DOM構(gòu)建過程

瀏覽器從網(wǎng)絡(luò)或者硬盤中獲取HTML字節(jié)數(shù)據(jù)后會經(jīng)過一個流程將字節(jié)解析成DOM樹,流程如下:

  • 編碼:先將HTML的原始字節(jié)數(shù)據(jù)轉(zhuǎn)換為文件指定編碼的字符笛园;

  • 令牌化:然后瀏覽器會根據(jù)HTML規(guī)范來將字符串轉(zhuǎn)換成各種令牌隘击;

    <html><body>這樣的標(biāo)簽以及標(biāo)簽中的字符串和屬性等都會被轉(zhuǎn)化為令牌研铆,每個令牌具有特殊含義的一組規(guī)則埋同。

    令牌記錄了標(biāo)簽的開始與結(jié)束,通過這個特性可以輕松判斷一個標(biāo)簽是否為子標(biāo)簽(假設(shè)有<html><body>兩個標(biāo)簽蚜印,當(dāng)<html>標(biāo)簽的令牌還沒遇到它的結(jié)束令牌</html>就遇見了<body>標(biāo)簽令牌莺禁,那么<body>就是<html>的子標(biāo)簽)。

  • 生成對象:接下來每個令牌都會被轉(zhuǎn)換成定義其屬性和規(guī)則的對象窄赋,這個對象就是節(jié)點對象哟冬;

  • 構(gòu)建完成DOM樹構(gòu)建完成,整個對象集合就像是一棵樹形結(jié)構(gòu)忆绰。

    為什么DOM是一個樹形結(jié)構(gòu)浩峡?這是因為標(biāo)簽之間含有復(fù)雜的父子關(guān)系,樹形結(jié)構(gòu)正好可以詮釋這個關(guān)系错敢,同理CSSOM也是樹形結(jié)構(gòu)翰灾,層疊樣式也含有父子關(guān)系。例如:

    div p { font-size: 18px }
    

    會先尋找所有p標(biāo)簽并判斷它的父標(biāo)簽是否為div之后才會決定要不要采用這個樣式進(jìn)行渲染稚茅。

整個DOM樹的構(gòu)建過程其實就是:

字節(jié)->字符->令牌->節(jié)點對象->對象模型

在解析DOM過程中纸淮,會碰到幾類特殊的節(jié)點需要特殊處理:

  1. <style><link>標(biāo)簽以及具有內(nèi)聯(lián)樣式的標(biāo)簽亚享,交給CSSOM生成咽块;

  2. <script>標(biāo)簽。

    Javascript可以操作修改DOM結(jié)構(gòu)欺税,可以操作CSSOM修改節(jié)點樣式侈沪,就會導(dǎo)致了瀏覽器在解析DOM時候,一碰到<script>就會停止DOM的解析(CSS不會)晚凿,執(zhí)行完Javascript再返還控制權(quán)亭罪。

    事實上,Javascript執(zhí)行前不僅僅是停止了DOM的解析歼秽,它還必須等待CSS的解析完成应役。當(dāng)瀏覽器碰到<script>標(biāo)簽時,發(fā)現(xiàn)該元素前面的CSS還未解析完成,就會等它解析完成再去執(zhí)行箩祥。

    Javascript阻塞了DOM的解析呻惕,也阻塞了其后的CSS解析,整個解析進(jìn)程必須等待Javascript的完成才能夠繼續(xù)滥比,這就是JS阻塞頁面。一個<script>標(biāo)簽推遲了DOM做院、CSSOM的生成以及以后的所有渲染過程盲泛,從性能角度上看,將<script>放在頁面底部键耕,也就合情合理了寺滚。

CSSOM

全稱為Cascading Style Sheets Object Model層疊樣式表對象模型,它與DOM樹的含義相差不大屈雄,只不過他是CSS的對象集合村视。

CSSOMDOM是兩個獨立的數(shù)據(jù)結(jié)構(gòu)。

瀏覽器解析DOM的時候酒奶,遇到了<style>和內(nèi)聯(lián)樣式時候蚁孔,會根據(jù)樣式的聲明生成CSSOM,因為他們本身含有樣式內(nèi)容惋嚎。

而遇到了<link>標(biāo)簽時杠氢,瀏覽器會首先發(fā)送請求,待請求成功獲取外聯(lián)樣式后另伍,便會解析該外聯(lián)樣式鼻百,并就會像生成DOM樹一樣生成相應(yīng)的CSSOM樹。

由于CSSOM負(fù)責(zé)儲存渲染信息摆尝,瀏覽器就必須保證再合成渲染樹之前温艇,CSSOM是完備的,這種完備是指所有的CSS(內(nèi)聯(lián)堕汞、內(nèi)部勺爱、外部)都已經(jīng)下載完,并解析完臼朗,只有DOMCSSOM解析完全結(jié)束邻寿,瀏覽器才會進(jìn)入下一步的渲染,這就是傳說中的CSS阻塞渲染视哑。

CSS阻塞渲染意味著绣否,在CSSOM完備前,頁面將一直處理白屏狀態(tài)挡毅,這就是為什么樣式放在<head>標(biāo)簽中蒜撮,僅僅是為了更快的解析CSS,保證更快的首次渲染。

需要注意的是段磨,即使沒有編寫任何樣式聲明取逾,CSSOM依然會生成,默認(rèn)生成的CSSOM是瀏覽器自帶默認(rèn)樣式苹支。

構(gòu)建渲染樹

在構(gòu)建了DOMCSSOM樹之后砾隅,瀏覽器只是擁有了兩個獨立的對象集合,DOM樹描述了文檔的結(jié)構(gòu)與內(nèi)容债蜜,CSSOM樹描述了對文檔應(yīng)用的樣式規(guī)則晴埂,想要渲染出頁面,就需要將DOM樹和CSSOM樹結(jié)合在一起寻定,生成渲染樹儒洛。這顆樹就包含了頁面所有可見元素及其渲染信息。

  • 瀏覽器會先從DOM樹的根節(jié)點開始遍歷每個可見節(jié)點狼速;

    <script>琅锻、<link><meta>都屬于不可視節(jié)點向胡,另外恼蓬,display: none的節(jié)點也屬于不可視節(jié)點,沒必要渲染在頁面上捷枯,值得注意的是visibility: hidden屬性并不算是不可見屬性滚秩,它的語義是隱藏元素,但元素仍然占據(jù)著局部空間淮捆,所以會被渲染成一個空框郁油。

  • CSSOM中適配可視節(jié)點的樣式規(guī)則;
  • 計算這些樣式攀痊,將計算值應(yīng)用到可視節(jié)點上桐腌;
  • 渲染樹構(gòu)建完成,每個節(jié)點都是可見節(jié)點并且都含有其內(nèi)容和對應(yīng)規(guī)則的樣式苟径。

計算布局

渲染樹構(gòu)建完畢后案站,瀏覽器得到了每個可視節(jié)點的內(nèi)容與其樣式,下一步則需要計算每個節(jié)點在窗口內(nèi)的確切位置與大小棘街, 也就是布局階段蟆盐。

CSS采用了一種叫做盒子模型的思維模型來表示每個節(jié)點與其他元素之間的距離,盒子模型包括外邊距Margin遭殉、內(nèi)邊距Padding石挂、邊框Border、內(nèi)容Content险污。頁面中的每個標(biāo)簽其實都是一個個盒子痹愚。

布局階段會從渲染樹的根節(jié)點開始遍歷富岳,然后確定每個節(jié)點對象在頁面上的確切大小與位置,布局階段的輸出是一個盒子模型拯腮,它會精確地捕獲每個元素在屏幕內(nèi)的確切位置與大小窖式,所有相對的測量值也都會被轉(zhuǎn)換為屏幕內(nèi)的絕對像素值。

渲染

得到了渲染樹及其節(jié)點的布局信息动壤,瀏覽器便可以將最終的頁面渲染到屏幕萝喘。


渲染阻塞的優(yōu)化方案

瀏覽器想要渲染一個頁面就必須先構(gòu)建出DOM樹和CSSOM樹,如果HTMLCSS文件結(jié)構(gòu)非常龐大與復(fù)雜琼懊,這顯然會給頁面加載速度帶來嚴(yán)重影響蜒灰。

所謂渲染阻塞資源,即是對該資源發(fā)送請求后還需要先構(gòu)建對應(yīng)的DOM樹或CSSOM樹肩碟,這種行為顯然會延遲渲染操作的開始時間。HTML凸椿、CSS削祈、JavaScript都是會對渲染產(chǎn)生阻塞的資源,HTML是必需的脑漫,但還可以從CSSJavaScript著手優(yōu)化髓抑,盡可能地減少阻塞的產(chǎn)生。

優(yōu)化CSS

如果可以讓CSS資源只在特定的條件下使用优幸,可以在首次加載時先不進(jìn)行構(gòu)建CSSOM樹吨拍,只有在特定條件下,才會讓瀏覽器進(jìn)行阻塞渲染然后構(gòu)建CSSOM网杆。

比如:

CSS的媒體查詢用來實現(xiàn)某些功能或者場景的羹饰,它由媒體類型以及零個或多個檢查特定媒體特征狀況的表達(dá)式組成。

<!-- 沒有使用媒體查詢碳却,這個css資源會阻塞渲染  -->
<link href="style.css" rel="stylesheet">
<!-- all是默認(rèn)類型队秩,它和不設(shè)置媒體查詢的效果是一樣的 -->
<link href="style.css" rel="stylesheet" media="all">
<!-- 動態(tài)媒體查詢, 將在網(wǎng)頁加載時計算昼浦。
根據(jù)網(wǎng)頁加載時設(shè)備的方向馍资,portrait.css 可能阻塞渲染,也可能不阻塞渲染关噪。-->
<link href="portrait.css" rel="stylesheet" media="orientation:portrait">
<!-- 只在打印網(wǎng)頁時應(yīng)用鸟蟹,因此網(wǎng)頁首次在瀏覽器中加載時,它不會阻塞渲染使兔。 -->
<link href="print.css" rel="stylesheet" media="print">

使用媒體查詢可以讓CSS資源不在首次加載中阻塞渲染建钥,但不管是哪種CSS資源它們的下載請求都不會被忽略,瀏覽器仍然會先下載CSS文件

優(yōu)化Javascript

當(dāng)瀏覽器的HTML解析器遇到一個<script>標(biāo)記時會暫停構(gòu)建DOM火诸,然后將控制權(quán)移交至JavaScript引擎锦针,這時引擎會開始執(zhí)行JavaScript腳本,直到執(zhí)行結(jié)束后,瀏覽器才會從之前中斷的地方恢復(fù)奈搜,然后繼續(xù)構(gòu)建DOM悉盆。每次去執(zhí)行JavaScript腳本都會嚴(yán)重地阻塞DOM樹的構(gòu)建,如果JavaScript腳本還操作了CSSOM馋吗,而正好這個CSSOM還沒有下載和構(gòu)建焕盟,瀏覽器甚至?xí)舆t腳本執(zhí)行和構(gòu)建DOM,直至完成其CSSOM的下載和構(gòu)建宏粤。

使用async可以通知瀏覽器該腳本不需要在引用位置執(zhí)行脚翘,這樣瀏覽器就可以繼續(xù)構(gòu)建DOMJavaScript腳本會在就緒后開始執(zhí)行绍哎,這樣將顯著提升頁面首次加載的性能来农。

<!-- 下面2個用法效果是等價的 -->
<script type="text/javascript" src="demo_async.js" async="async"></script>
<script type="text/javascript" src="demo_async.js" async></script>

優(yōu)化關(guān)鍵渲染路徑

優(yōu)化關(guān)鍵渲染路徑就是在對關(guān)鍵資源、關(guān)鍵路徑長度和關(guān)鍵字節(jié)進(jìn)行優(yōu)化崇堰。關(guān)鍵資源越少沃于,瀏覽器在渲染前的準(zhǔn)備工作就越少;同樣海诲,關(guān)鍵路徑長度和關(guān)鍵字節(jié)關(guān)系到瀏覽器下載資源的效率繁莹,它們越少,瀏覽器下載資源的速度就越快特幔。

加載部分HTML

服務(wù)端在接收到請求時先只響應(yīng)回HTML的初始部分咨演,后續(xù)的HTML內(nèi)容在需要時再通過AJAX獲得。由于服務(wù)端只發(fā)送了部分HTML文件蚯斯,這讓構(gòu)建DOM樹的工作量減少很多薄风,從而讓用戶感覺頁面的加載速度很快。

注意拍嵌,這個方法不能用在CSS上村刨,瀏覽器不允許CSSOM只構(gòu)建初始部分,否則會無法確定具體的樣式撰茎。

壓縮

通過對外部資源進(jìn)行壓縮可以大幅度地減少瀏覽器需要下載的資源量嵌牺,它會減少關(guān)鍵路徑長度與關(guān)鍵字節(jié),使頁面的加載速度變得更快龄糊。

對數(shù)據(jù)進(jìn)行壓縮其實就是使用更少的位數(shù)來對數(shù)據(jù)進(jìn)行重編碼逆粹。

在對HTMLCSSJavaScript這些文件進(jìn)行壓縮之前炫惩,還需要先進(jìn)行一次冗余壓縮僻弹。所謂冗余壓縮,就是去除多余的字符他嚷,例如注釋蹋绽、空格符和換行符芭毙。這些字符對于程序員是有用的,畢竟沒有格式化的代碼可讀性是非承对牛恐怖的退敦,但它們對于瀏覽器是沒有任何意義的,去除這些冗余可以減少文件的數(shù)據(jù)量蚣抗。在進(jìn)行完冗余壓縮之后侈百,再使用壓縮算法進(jìn)一步對數(shù)據(jù)本身進(jìn)行壓縮,例如GZIPGZIP是一個可以作用于任何字節(jié)流的通用壓縮算法翰铡,它會記憶之前已經(jīng)看到的內(nèi)容钝域,然后再嘗試查找并替換重復(fù)的內(nèi)容。)锭魔。

HTTP緩存

通過網(wǎng)絡(luò)來獲取資源通常是緩慢的例证,如果資源文件過于膨大,瀏覽器還需要與服務(wù)器之間進(jìn)行多次往返通信才能獲得完整的資源文件迷捧。緩存可以復(fù)用之前獲取的資源战虏,既然后端可以使用緩存來減少訪問數(shù)據(jù)庫的開銷,那前端自然也可以使用緩存來復(fù)用資源文件党涕。

資源預(yù)加載

Pre-fetching是一種提示瀏覽器預(yù)先加載用戶之后可能會使用到的資源的方法。

使用dns-prefetch來提前進(jìn)行DNS解析巡社,以便之后可以快速地訪問另一個主機名(瀏覽器會在加載網(wǎng)頁時對網(wǎng)頁中的域名進(jìn)行解析緩存膛堤,這樣你在之后的訪問時無需進(jìn)行額外的DNS解析,減少了用戶等待時間晌该,提高了頁面加載速度)肥荔。

<link rel="dns-prefetch" href="other.hostname.com">

使用prefetch屬性可以預(yù)先下載資源,不過它的優(yōu)先級是最低的朝群。

<link rel="prefetch"  href="/some_other_resource.jpeg">

Chrome允許使用subresource屬性指定優(yōu)先級最高的下載資源(當(dāng)所有屬性為subresource的資源下載完完畢后燕耿,才會開始下載屬性為prefetch的資源)。

<link rel="subresource"  href="/some_other_resource.js">

prerender可以預(yù)先渲染好頁面并隱藏起來姜胖,之后打開這個頁面會跳過渲染階段直接呈現(xiàn)在用戶面前(推薦對用戶接下來必須訪問的頁面進(jìn)行預(yù)渲染誉帅,否則得不償失)。

<link rel="prerender"  >

參考文獻(xiàn)

Web Fundamentals | Google Developers

Flushing the Document Early | High Performance Web Sites

瀏覽器渲染過程與性能優(yōu)化

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末右莱,一起剝皮案震驚了整個濱河市蚜锨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌慢蜓,老刑警劉巖亚再,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異晨抡,居然都是意外死亡氛悬,警方通過查閱死者的電腦和手機则剃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來如捅,“玉大人棍现,你說我怎么就攤上這事∥毙啵” “怎么了轴咱?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長烈涮。 經(jīng)常有香客問我朴肺,道長,這世上最難降的妖魔是什么坚洽? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任戈稿,我火速辦了婚禮,結(jié)果婚禮上讶舰,老公的妹妹穿的比我還像新娘鞍盗。我一直安慰自己,他們只是感情好跳昼,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布般甲。 她就那樣靜靜地躺著,像睡著了一般鹅颊。 火紅的嫁衣襯著肌膚如雪敷存。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天堪伍,我揣著相機與錄音锚烦,去河邊找鬼。 笑死帝雇,一個胖子當(dāng)著我的面吹牛涮俄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播尸闸,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼彻亲,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吮廉?” 一聲冷哼從身側(cè)響起睹栖,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎茧痕,沒想到半個月后野来,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡踪旷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年曼氛,在試婚紗的時候發(fā)現(xiàn)自己被綠了豁辉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡舀患,死狀恐怖徽级,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情聊浅,我是刑警寧澤餐抢,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站低匙,受9級特大地震影響旷痕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜顽冶,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一欺抗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧强重,春花似錦绞呈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至倘要,卻和暖如春圾亏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背碗誉。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留父晶,地道東北人哮缺。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像甲喝,于是被迫代替她去往敵國和親尝苇。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 發(fā)送 & 接收信息 數(shù)據(jù)是以“數(shù)據(jù)包”的形式通過互聯(lián)網(wǎng)發(fā)送埠胖,而數(shù)據(jù)包以字節(jié)為單位糠溜。當(dāng)你編寫一些 HTML、CSS ...
    mongofeng閱讀 903評論 0 0
  • 發(fā)送 & 接收信息 數(shù)據(jù)是以“數(shù)據(jù)包”的形式通過互聯(lián)網(wǎng)發(fā)送直撤,而數(shù)據(jù)包以字節(jié)為單位非竿。當(dāng)你編寫一些 HTML、CSS ...
    IT界中小PQ閱讀 405評論 0 0
  • 前言 瀏覽器的內(nèi)核是指支持瀏覽器運行的最核心的程序谋竖,分為兩個部分的红柱,一是渲染引擎承匣,另一個是JS引擎。渲染引擎在不同...
    浪里行舟閱讀 1,521評論 0 13
  • 大家都知道萬維網(wǎng)的應(yīng)用層使用了HTTP協(xié)議锤悄,并且用瀏覽器作為入口訪問網(wǎng)絡(luò)上的資源韧骗。用戶在使用瀏覽器訪問一個網(wǎng)站時需...
    SylvanasSun閱讀 2,139評論 1 12
  • 作者: 前端工匠 公號 / 浪里行舟 (本文來自作者投稿) 前言 瀏覽器的內(nèi)核是指支持瀏覽器運行的最核心的程序,分...
    一個敲代碼的前端妹子閱讀 724評論 1 5