瀏覽器的渲染機(jī)制及優(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ù)器返回的HTML、CSSJavascript字節(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é)點(diǎn)和對象組成的集合,可以說一個WEB頁面其實(shí)就是一個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é)點(diǎn)對象;

  • 構(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)建過程其實(shí)就是:

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

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

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

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

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

    事實(shí)上,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)簽推遲了DOMCSSOM的生成以及以后的所有渲染過程,從性能角度上看,將<script>放在頁面底部,也就合情合理了。

CSSOM

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

CSSOMDOM是兩個獨(dú)立的數(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樹之后,瀏覽器只是擁有了兩個獨(dú)立的對象集合,DOM樹描述了文檔的結(jié)構(gòu)與內(nèi)容,CSSOM樹描述了對文檔應(yīng)用的樣式規(guī)則,想要渲染出頁面,就需要將DOM樹和CSSOM樹結(jié)合在一起,生成渲染樹。這顆樹就包含了頁面所有可見元素及其渲染信息。

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

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

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

計算布局

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

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

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

渲染

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


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

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

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

優(yōu)化CSS

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

比如:

CSS的媒體查詢用來實(shí)現(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)建DOM,JavaScript腳本會在就緒后開始執(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ù)來對數(shù)據(jù)進(jìn)行重編碼。

在對HTML、CSSJavaScript這些文件進(jìn)行壓縮之前,還需要先進(jìn)行一次冗余壓縮。所謂冗余壓縮,就是去除多余的字符,例如注釋、空格符和換行符。這些字符對于程序員是有用的,畢竟沒有格式化的代碼可讀性是非??植赖模鼈儗τ跒g覽器是沒有任何意義的,去除這些冗余可以減少文件的數(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解析,以便之后可以快速地訪問另一個主機(jī)名(瀏覽器會在加載網(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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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

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