瀏覽器渲染頁面原理及過程,重構重繪優化

一、爲什麼要了解瀏覽器加載、解析、渲染這個過程?

瞭解瀏覽器如何進行加載,我們可以在引用外部樣式文件,外部js時,將他們放到合適的位置,使瀏覽器以最快的速度將文件加載完畢。

瞭解瀏覽器如何進行解析,我們可以在構建DOM結構,組織css選擇器時,選擇最優的寫法,提高瀏覽器的解析速率。

瞭解瀏覽器如何進行渲染,明白渲染的過程,我們在設置元素屬性,編寫js文件時,可以減少”重繪“”重新佈局“的消耗。

這三個過程在實際進行的時候又不是完全獨立,而是會有交叉。會造成一邊加載,一邊解析,一邊渲染的工作現象。


二、瀏覽器是如何進行加載、解析、渲染的呢?

WEB 頁面運行在各種各樣的瀏覽器當中,瀏覽器載入、渲染頁面的速度直接影響着用戶體驗簡單地說,頁面渲染就是瀏覽器將 HTML 代碼根據 CSS 定義的規則顯示在瀏覽器窗口中的這個過程。

1. 用戶輸入網址(假設是個 HTML 頁面,並且是第一次訪問),瀏覽器向服務器發出請求,服務器返回 HTML 文件;

2. 瀏覽器開始載入 HTML 代碼,發現 <head> 標籤內有一個 <link> 標籤引用外部 CSS 文件;

3. 瀏覽器又發出 CSS 文件的請求,服務器返回這個 CSS 文件(異步線程);

4. 瀏覽器繼續載入 HTML 中 <body> 部分的代碼,並且 CSS 文件已經拿到手了,可以開始渲染頁面了;

5. 瀏覽器在代碼中發現一個 <img> 標籤引用了一張圖片,向服務器發出請求。此時瀏覽器不會等到圖片下載完,而是繼續渲染後面的代碼;(異步線程)

6. 服務器返回圖片文件,由於圖片佔用了一定面積,影響了後面段落的排布,因此瀏覽器需要回過頭來重新渲染這部分代碼;

7. 瀏覽器發現了一個包含一行 JavaScript 代碼的 <script> 標籤,趕快運行它,必須等它執行完後面的dom纔可以繼續解析;

8. JavaScript 腳本執行了這條語句,它命令瀏覽器隱藏掉代碼中的某個 <div>(style.display=”none”)。杯具啊,突然就少了這麼一個元素,瀏覽器不得不重新渲染這部分代碼;

9. 終於等到了 </html> 的到來,瀏覽器淚流滿面……

10. 等等還沒完,用戶點了一下界面中的“換膚”按鈕,JavaScript 讓瀏覽器換了一下 <link> 標籤的 CSS 路徑;

11. 瀏覽器召集了在座的各位 <div><span><ul><li> 們,“大夥兒收拾收拾行李,咱得重新來過……”,瀏覽器向服務器請求了新的CSS文件,重新渲染頁面。

 

 可總結爲如下四個大步驟

1、構建DOM樹(parse):渲染引擎解析HTML文檔,首先將標籤轉換成DOM樹中的DOM node(包括js生成的標籤)生成內容樹(Content Tree/DOM Tree);
2、構建渲染樹(construct):解析對應的CSS樣式文件信息(包括js生成的樣式和外部css文件),而這些文件信息以及HTML中可見的指令(如),構建渲染樹(Rendering Tree/Frame Tree);
3、佈局渲染樹(reflow/layout):從根節點遞歸調用,計算每一個元素的大小、位置等,給出每個節點所應該在屏幕上出現的精確座標;
4、繪製渲染樹(paint/repaint):遍歷渲染樹,使用UI後端層來繪製每個節點。

整體構建過程參考圖

Webkit渲染引擎流程如下圖:

è¿éåå¾çæè¿°

Gecko渲染引擎流程如下圖:

è¿éåå¾çæè¿°

 

如上圖,Webkit瀏覽器和Gecko瀏覽器渲染流程大致相同,不同的是:

1.Webkit瀏覽器中的渲染樹(render tree),在Gecko瀏覽器中對應的則是框架樹(frame tree),渲染對象(render object)對應的是框架(frame);
2.Webkit中的佈局(Layout)過程,在Gecko中稱爲迴流(Reflow),本質是一樣的,後文會解釋迴流的另一層含義–重新佈局;
3.Gecko中HTML和DOM樹中間多了一層內容池(Content sink),可以理解成生成DOM元素的工廠。

JS操作重構重繪過程圖解:

瀏覽器加載 JavaScript 腳本,主要通過 <script> 元素完成。正常的網頁加載流程是這樣的。

1. 瀏覽器一邊下載 HTML 網頁,一邊開始解析。也就是說,不等到下載完,就開始解析。
2. 解析過程中,瀏覽器發現 <script> 元素,就暫停解析,把網頁渲染的控制權轉交給 JavaScript 引擎。
3. 如果 <script> 元素引用了外部腳本,就下載該腳本再執行,否則就直接執行代碼。
4. JavaScript 引擎執行完畢,控制權交還渲染引擎,恢復往下解析 HTML 網頁。

加載外部腳本時,瀏覽器會暫停頁面渲染,等待腳本下載並執行完成後,再繼續渲染。原因是JavaScript 代碼可以修改 DOM,所以必須把控制權讓給它,否則會導致複雜的線程競賽的問題。


如果外部腳本加載時間很長(一直無法完成下載),那麼瀏覽器就會一直等待腳本下載完成,造成網頁長時間失去響應,瀏覽器就會呈現“假死”狀態,這被稱爲“阻塞效應”。


爲了避免這種情況,較好的做法是將 <script> 標籤都放在頁面底部,而不是頭部。這樣即使遇到腳本失去響應,網頁主體的渲染也已經完成了,用戶至少可以看到內容,而不是面對一張空白的頁面。如果某些腳本代碼非常重要,一定要放在頁面頭部的話,最好直接將代碼寫入頁面,而不是連接外部腳本文件,這樣能縮短加載時間。


腳本文件都放在網頁尾部加載,還有一個好處。因爲在 DOM 結構生成之前就調用 DOM 節點,JavaScript 會報錯,如果腳本都在網頁尾部加載,就不存在這個問題,因爲這時 DOM 肯定已經生成了。 

三  影響頁面渲染速度主要有哪些?

影響頁面渲染速度主要有:重排(重構/迴流/reflow)和重繪(repaint或redraw)

重繪(repaint或redraw):當盒子的位置、大小以及其他屬性,例如顏色、字體大小等都確定下來之後,瀏覽器便把這些原色都按照各自的特性繪製一遍,將內容呈現在頁面上。

重繪是指一個元素外觀的改變所觸發的瀏覽器行爲,瀏覽器會根據元素的新屬性重新繪製,使元素呈現新的外觀。觸發重繪的條件:改變元素外觀屬性。如:color,background-color等。

注意:table及其內部元素可能需要多次計算才能確定好其在渲染樹中節點的屬性值,比同等元素要多花兩倍時間,這就是我們儘量避免使用table佈局頁面的原因之一。

重排(重構/迴流/reflow):當渲染樹中的一部分(或全部)因爲元素的規模尺寸,佈局,隱藏等改變而需要重新構建, 這就稱爲迴流(reflow)。每個頁面至少需要一次迴流,就是在頁面第一次加載的時候。

重繪和重排的關係:在迴流的時候,瀏覽器會使渲染樹中受到影響的部分失效,並重新構造這部分渲染樹,完成迴流後,瀏覽器會重新繪製受影響的部分到屏幕中,該過程稱爲重繪。

所以,重排必定會引發重繪,但重繪不一定會引發重排。

觸發重排的條件:任何頁面佈局和幾何屬性的改變都會觸發重排,比如:

1、頁面渲染初始化;(無法避免)

2、添加或刪除可見的DOM元素;

3、元素位置的改變,或者使用動畫;

4、元素尺寸的改變——大小,外邊距,邊框;

5、瀏覽器窗口尺寸的變化(resize事件發生時);

6、填充內容的改變,比如文本的改變或圖片大小改變而引起的計算值寬度和高度的改變;

7、讀取某些元素屬性:(offsetLeft/Top/Height/Width, clientTop/Left/Width/Height, scrollTop/Left/Width/Height, width/height, getComputedStyle(), currentStyle(IE) )

重繪重排的代價:耗時,導致瀏覽器卡慢。

四 如何優化呢?

1、瀏覽器自己的優化:瀏覽器會維護1個隊列,把所有會引起迴流、重繪的操作放入這個隊列,等隊列中的操作到了一定的數量或者到了一定的時間間隔,瀏覽器就會flush隊列,進行一個批處理。這樣就會讓多次的迴流、重繪變成一次迴流重繪。

2、我們要注意的優化:我們要減少重繪和重排就是要減少對渲染樹的操作,則我們可以合併多次的DOM和樣式的修改。並減少對style樣式的請求。

(1)直接改變元素的className

(2)display:none;先設置元素爲display:none;然後進行頁面佈局等操作;設置完成後將元素設置爲display:block;這樣的話就只引發兩次重繪和重排;

(3)不要經常訪問瀏覽器的flush隊列屬性;如果一定要訪問,可以利用緩存。將訪問的值存儲起來,接下來使用就不會再引發迴流;

(4)使用cloneNode(true or false) 和 replaceChild 技術,引發一次迴流和重繪;

(5)將需要多次重排的元素,position屬性設爲absolute或fixed,元素脫離了文檔流,它的變化不會影響到其他元素;

(6)如果需要創建多個DOM節點,可以使用DocumentFragment創建完後一次性的加入document;

 

 

 

發佈了49 篇原創文章 · 獲贊 6 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章