以前大概知道個流程,如今梳理下 印象深入。javascript
要了解瀏覽器渲染頁面的過程,首先得知道一個名詞——關鍵渲染路徑。關鍵渲染路徑是指瀏覽器從最初接收請求來的HTML、CSS、javascript等資源,而後解析、構建樹、渲染布局、繪製,最後呈現給客戶能看到的界面這整個過程。
用戶看到頁面實際上能夠分爲兩個階段:頁面內容加載完成和頁面資源加載完成,分別對應於DOMContentLoaded
和Load
。css
DOMContentLoaded
事件觸發時,僅當DOM加載完成,不包括樣式表,圖片等load
事件觸發時,頁面上全部的DOM,樣式表,腳本,圖片都已加載完成, load事件時間上比$(document).ready()還後面瀏覽器渲染的過程主要包括如下五步:html
rendering tree
)將會被建立,表明一系列將被渲染的對象。layout
。瀏覽器使用一種流式處理的方法,只須要一次繪製操做就能夠佈局全部的元素。painting
。須要注意的是,以上五個步驟並不必定一次性順序完成,好比DOM或CSSOM被修改時,亦或是哪一個過程會重複執行,這樣才能計算出哪些像素須要在屏幕上進行從新渲染。而在實際狀況中,JavaScript和CSS的某些操做每每會屢次修改DOM或者CSSOM。java
當瀏覽器接收到服務器響應來的HTML文檔後,會遍歷文檔節點,生成DOM樹。
須要注意如下幾點:node
display:none
的元素也會在DOM樹中script
標籤會在DOM樹中不管是DOM仍是CSSOM,都是要通過Bytes→characters→tokens→nodes→objectmodel
這個過程。linux
當前節點的全部子節點都構建好後纔會去構建當前節點的下一個兄弟節點。web
瀏覽器解析CSS文件並生成CSSOM,每一個CSS文件都被分析成一個StyleSheet對象,每一個對象都包含CSS規則。CSS規則對象包含對應於CSS語法的選擇器和聲明對象以及其餘對象。
在這個過程須要注意的是:後端
script
的執行互斥 。script
執行優化,只有在JS訪問CSS時纔會發生互斥。經過DOM樹和CSS規則樹,瀏覽器就能夠經過它兩構建渲染樹了。瀏覽器會先從DOM樹的根節點開始遍歷每一個可見節點,而後對每一個可見節點找到適配的CSS樣式規則並應用。
有如下幾點須要注意:瀏覽器
display: none
的元素不在Render Tree中visibility: hidden
的元素在Render Tree中渲染樹生成後,仍是沒有辦法渲染到屏幕上,渲染到屏幕須要獲得各個節點的位置信息,這就須要佈局(Layout)的處理了。緩存
佈局階段會從渲染樹的根節點開始遍歷,因爲渲染樹的每一個節點都是一個Render Object對象,包含寬高,位置,背景色等樣式信息。因此瀏覽器就能夠經過這些樣式信息來肯定每一個節點對象在頁面上的確切大小和位置,佈局階段的輸出就是咱們常說的盒子模型,它會精確地捕獲每一個元素在屏幕內的確切位置與大小。須要注意的是:
float
元素,absoulte
元素,fixed
元素會發生位置偏移。在繪製階段,瀏覽器會遍歷渲染樹,調用渲染器的paint()
方法在屏幕上顯示其內容。渲染樹的繪製工做是由瀏覽器的UI後端組件完成的。
渲染引擎主要有兩個:webkit和Gecko
Firefox使用Geoko,Mozilla自主研發的渲染引擎。Safari和Chrome都使用webkit。Webkit是一款開源渲染引擎,它原本是爲linux平臺研發的,後來由Apple移植到Mac及Windows上。
雖然主流瀏覽器渲染過程叫法有區別,可是主要流程仍是相同的。
JS能夠操做DOM來修改DOM結構,能夠操做CSSOM來修改節點樣式,這就致使了瀏覽器在遇到<script>
標籤時,DOM構建將暫停,直至腳本完成執行,而後繼續構建DOM。若是腳本是外部的,會等待腳本下載完畢,再繼續解析文檔。如今能夠在script
標籤上增長屬性defer
或者async
。腳本解析會將腳本中改變DOM和CSS的地方分別解析出來,追加到DOM樹和CSSOM規則樹上。
每次去執行JavaScript腳本都會嚴重地阻塞DOM樹的構建,若是JavaScript腳本還操做了CSSOM,而正好這個CSSOM尚未下載和構建,瀏覽器甚至會延遲腳本執行和構建DOM,直至完成其CSSOM的下載和構建。因此,script
標籤的位置很重要。
JS阻塞了構建DOM樹,也阻塞了其後的構建CSSOM規則樹,整個解析進程必須等待JS的執行完成纔可以繼續,這就是所謂的JS阻塞頁面。
因爲CSSOM負責存儲渲染信息,瀏覽器就必須保證在合成渲染樹以前,CSSOM是完備的,這種完備是指全部的CSS(內聯、內部和外部)都已經下載完,並解析完,只有CSSOM和DOM的解析徹底結束,瀏覽器纔會進入下一步的渲染,這就是CSS阻塞渲染。
CSS阻塞渲染意味着,在CSSOM完備前,頁面將一直處理白屏狀態,這就是爲何樣式放在head
中,僅僅是爲了更快的解析CSS,保證更快的首次渲染。
須要注意的是,即使你沒有給頁面任何的樣式聲明,CSSOM依然會生成,默認生成的CSSOM自帶瀏覽器默認樣式。
當解析HTML的時候,會把新來的元素插入DOM樹裏面,同時去查找CSS,而後把對應的樣式規則應用到元素上,查找樣式表是按照從右到左的順序去匹配的。
例如:div p {font-size: 16px}
,會先尋找全部p
標籤並判斷它的父標籤是否爲div
以後纔會決定要不要採用這個樣式進行渲染)。
因此,咱們平時寫CSS時,儘可能用id
和class
,千萬不要過渡層疊。
咱們都知道HTML默認是流式佈局的,但CSS和JS會打破這種佈局,改變DOM的外觀樣式以及大小和位置。所以咱們就須要知道兩個概念:replaint
和reflow
。
reflow(迴流)
當瀏覽器發現佈局發生了變化,這個時候就須要倒回去從新渲染,你們稱這個回退的過程叫reflow
。reflow
會從html
這個root frame
開始遞歸往下,依次計算全部的結點幾何尺寸和位置,以確認是渲染樹的一部分發生變化仍是整個渲染樹。reflow
幾乎是沒法避免的,由於只要用戶進行交互操做,就勢必會發生頁面的一部分的從新渲染,且一般咱們也沒法預估瀏覽器到底會reflow
哪一部分的代碼,由於他們會相互影響。
repaint(重繪)
repaint
則是當咱們改變某個元素的背景色、文字顏色、邊框顏色等等不影響它周圍或內部佈局的屬性時,屏幕的一部分要重畫,可是元素的幾何尺寸和位置沒有發生改變。
須要注意的是,display:none
會觸發reflow
,而visibility: hidden
屬性則並不算是不可見屬性,它的語義是隱藏元素,但元素仍然佔據着佈局空間,它會被渲染成一個空框。因此visibility:hidden
只會觸發repaint
,由於沒有發生位置變化。
另外有些狀況下,好比修改了元素的樣式,瀏覽器並不會馬上reflow
或repaint
一次,而是會把這樣的操做積攢一批,而後作一次reflow
,這又叫異步reflow
或增量異步reflow
。可是在有些狀況下,好比resize
窗口,改變了頁面默認的字體等。對於這些操做,瀏覽器會立刻進行reflow
。
引發reflow
現代瀏覽器會對迴流作優化,它會等到足夠數量的變化發生,再作一次批處理迴流。
padding
改變)resize
瀏覽器爲了得到正確的值也會提早觸發迴流,這樣就使得瀏覽器的優化失效了,這些屬性包括offsetLeft、offsetTop、offsetWidth、offsetHeight、 scrollTop/Left/Width/Height、clientTop/Left/Width/Height
、調用了getComputedStyle()
。
引發repaint
reflow
迴流一定引發repaint
重繪,重繪能夠單獨觸發。
背景色、顏色、字體改變(注意:字體大小發生變化時,會觸發迴流)
減小reflow、repaint觸發次數
transform
作形變和位移能夠減小reflow
DocumentFragment
將須要屢次修改的DOM元素緩存,最後一次性append
到真實DOM中渲染display:none
,操做完再顯示。(由於隱藏元素不在render
樹內,所以修改隱藏元素不會觸發迴流重繪)結合上文有如下幾點能夠優化渲染效率。
head
標籤中,而腳本文件在body
結束前,這樣能夠防止阻塞的方式。class
,或者csstext
屬性,一次性地改變樣式。transform
來作形變和位移Document Fragment
對象,完成後再把這個對象加入DOM。再好比,使用cloneNode()
方法,在克隆的節點上進行操做,而後再用克隆的節點替換原始節點。display: none
(須要1次重排和重繪),而後對這個節點進行100次操做,最後再恢復顯示(須要1次重排和重繪)。這樣一來,你就用兩次從新渲染,取代了可能高達100次的從新渲染。position
屬性爲absolute
或fixed
的元素,重排的開銷會比較小,由於不用考慮它對其餘元素的影響。display
屬性爲可見,由於不可見的元素不影響重排和重繪。另外,visibility : hidden
的元素只對重繪有影響,不影響重排。window.requestAnimationFrame()
、window.requestIdleCallback()
這兩個方法調節從新渲染。