要了解瀏覽器渲染頁面的過程,首先得知道一個名詞——關鍵路徑渲染。關鍵渲染路徑(Critical Rendering Path)是指與當前用戶操做有關的內容。例如用戶在瀏覽器中打開一個頁面,其中頁面所顯示的東西就是當前用戶操做相關的內容,也就是瀏覽器從服務器那收到的HTML,CSS,JavaScript等相關資源,而後通過一系列處理後渲染出來的web頁面。html
而瀏覽器渲染的過程主要包括如下五步:web
瀏覽器將獲取的HTML文檔並解析成DOM樹。後端
處理CSS標記,構成層疊樣式表模型CSSOM(CSS Object Model)。瀏覽器
將DOM和CSSOM合併爲渲染樹(rendering tree)將會被建立,表明一系列將被渲染的對象。服務器
渲染樹的每一個元素包含的內容都是計算過的,它被稱之爲佈局layout。瀏覽器使用一種流式處理的方法,只須要一次pass繪製操做就能夠佈局全部的元素。異步
將渲染樹的各個節點繪製到屏幕上,這一步被稱爲繪製painting.佈局
具體以下圖過程以下圖所示:性能
Webkit字體
須要注意的是,以上五個步驟並不必定一次性順序完成,好比DOM或CSSOM被修改時,亦或是哪一個過程會重複執行,這樣才能計算出哪些像素須要在屏幕上進行從新渲染。而在實際狀況中,JavaScript和CSS的某些操做每每會屢次修改DOM或者CSSOM。優化
下面咱們就來詳細的瞭解一下這幾個過程及須要注意的事項。
當瀏覽器客戶端從服務器那接受到HTML文檔後,就會遍歷文檔節點而後生成DOM樹,DOM樹結構和HTML標籤一一對應。須要注意記下幾點:
DOM樹在構建的過程當中可能會被CSS和JS的加載而執行阻塞。(這在後面會詳細介紹。)
display:none 的元素也會在DOM樹中。
註釋也會在DOM樹中
script標籤會在DOM樹中
瀏覽器會解析CSS文件並生成CSS規則樹,在過程當中,每一個CSS文件都會被分析成StyleSheet對象,每一個對象都包括CSS規則,CSS規則對象包括對應的選擇器和聲明對象以及其餘對象。
在這個過程須要注意的是:
CSS解析能夠與DOM解析同進行。
CSS解析與script的執行互斥 。
在Webkit內核中進行了script執行優化,只有在JS訪問CSS時纔會發生互斥。
經過DOM樹和CSS規則樹,瀏覽器就能夠經過它兩構建渲染樹了。瀏覽器會先從DOM樹的根節點開始遍歷每一個可見節點,讓後對每一個可見節點找到適配的CSS樣式規則並應用。具體的規則有如下幾點須要注意:
Render Tree和DOM Tree不徹底對應。
display: none的元素不在Render Tree中
visibility: hidden的元素在Render Tree中
佈局階段會從渲染樹的更節點開始遍歷,因爲渲染樹的每一個節點都是一個Render Object對象,包含寬高,位置,背景色等樣式信息。因此瀏覽器就能夠經過這些樣式信息來肯定每一個節點對象在頁面上的確切大小和位置,佈局階段的輸出就是咱們常說的盒子模型,它會精確地捕獲每一個元素在屏幕內的確切位置與大小。須要注意的是:
float元素,absoulte元素,fixed元素會發生位置偏移。
咱們常說的脫離文檔流,其實就是脫離Render Tree。
在繪製階段,瀏覽器會遍歷渲染樹,調用渲染器的paint()方法在屏幕上顯示其內容。渲染樹的繪製工做是由瀏覽器的UI後端組件完成的。
前面咱們有提到這方面的問題,說到資源的阻塞咱們清楚的是,現代瀏覽器老是並行加載自語言。例如當HTML解析器被腳本阻塞時,解析器雖然會中止構建DOM,但仍然會辨識該腳本後面的資源,並進行預加載。且因爲如下兩點。瀏覽器會延遲 JavaScript 的執行和 DOM 構建:
CSS 被默認被視爲阻塞渲染的資源,所以瀏覽器將在 CSSOM 構建完畢前不會渲染任何已處理的內容。
JavaScript 不只能夠讀取和修改 DOM 屬性,還能夠讀取和修改 CSSOM 屬性,所以CSS解析與script的執行互斥。
正是因爲以上這些緣由,script標籤的位置很重要咱們在實際開發中應該儘可能堅持如下兩個原則:
在引入順序上,CSS 資源先於 JavaScript 資源。
JavaScript 應儘可能少的去影響 DOM 的構建。
咱們都知道HTML默認是流式佈局的,但CSS和JS會打破這種佈局,改變DOM的外觀樣式以及大小和位置。所以咱們就須要知道兩個概念:
reflow(迴流):當瀏覽器發現某個部分發生了變化從而影響了佈局,這個時候就須要倒回去從新渲染,你們稱這個回退的過程叫 reflow。 常見的reflow是一些會影響頁面佈局的操做,諸如Tab,隱藏等。reflow 會從 html 這個 root frame 開始遞歸往下,依次計算全部的結點幾何尺寸和位置,以確認是渲染樹的一部分發生變化仍是整個渲染樹。reflow幾乎是沒法避免的,由於只要用戶進行交互操做,就勢必會發生頁面的一部分的從新渲染,且一般咱們也沒法預估瀏覽器到底會reflow哪一部分的代碼,由於他們會相互影響。
repaint(重繪): repaint則是當咱們改變某個元素的背景色、文字顏色、邊框顏色等等不影響它周圍或內部佈局的屬性時,屏幕的一部分要重畫,可是元素的幾何尺寸和位置沒有發生改變。
須要注意的是,display:none 會觸發 reflow,而visibility: hidden屬性則並不算是不可見屬性,它的語義是隱藏元素,但元素仍然佔據着佈局空間,它會被渲染成一個空框,這在咱們上面有提到過。因此visibility:hidden 只會觸發 repaint,由於沒有發生位置變化。
咱們不能避免reflow,但仍是能經過一些操做來減小回流:
用transform作形變和位移.
經過絕對位移來脫離當前層疊上下文,造成新的Render Layer。
另外有些狀況下,好比修改了元素的樣式,瀏覽器並不會馬上reflow 或 repaint 一次,而是會把這樣的操做積攢一批,而後作一次 reflow,這又叫異步 reflow 或增量異步 reflow。可是在有些狀況下,好比resize 窗口,改變了頁面默認的字體等。對於這些操做,瀏覽器會立刻進行 reflow。
結合上文和我看到的一些文章,有如下幾點能夠優化渲染效率
合法地去書寫 HTML 和 CSS ,且不要忘了文檔編碼類型。
樣式文件應當在 head 標籤中,而腳本文件在 body 結束前,這樣能夠防止阻塞的方式。
簡化並優化CSS選擇器,儘可能將嵌套層減小到最小。
儘可能減小在 JavaScript 中進行DOM操做。
修改元素樣式時,更改其class屬性是性能最高的方法。
儘可能用 transform 來作形變和位移