瀏覽器渲染原理

當咱們打開一個瀏覽器,輸入一個url,按下回車,不用多久,一個漂亮的頁面就呈如今咱們眼前了。其實,在咱們等待頁面打開的這段時間裏,瀏覽器但是作了很多的事情,究竟是什麼事情呢,就咱們一塊兒來看看吧css

首先,咱們須要先了解一下什麼是頁面渲染以及頁面渲染的整個過程~html

 

一、頁面渲染瀏覽器

簡單地說,頁面渲染就是瀏覽器將 HTML 代碼根據 CSS 定義的規則顯示在瀏覽器窗口中的這個過程服務器

二、渲染過程佈局

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

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

3. 瀏覽器又發出 CSS 文件的請求,服務器返回這個 CSS 文件;字體

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

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

6. 服務器返回圖片文件,因爲圖片佔用了必定面積,影響了後面段落的排布,所以瀏覽器須要回過頭來從新渲染這部分代碼;

7. 瀏覽器發現了一個包含一行 JavaScript 代碼的 <script> 標籤,趕快運行它;

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

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

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

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

 

如今你們已經知道什麼是頁面渲染了吧,接下來再介紹兩個概念,reflow和repaint~

三、reflow(迴流)

 

首先說一下頁面爲何會慢?那是由於瀏覽器要花時間、花精力去渲染,尤爲是當它發現某個部分發生了點變化影響了佈局,須要倒回去從新渲染,內行稱這個回退的過程叫 reflow

下面這張圖很清晰的說明了這個過程

reflow 幾乎是沒法避免的。如今界面上流行的一些效果,好比樹狀目錄的摺疊、展開(實質上是元素的顯 示與隱藏)等,都將引發瀏覽器的 reflow。鼠標滑過、點擊……只要這些行爲引發了頁面上某些元素的佔位面積、定位方式、邊距等屬性的變化,都會引發它內部、周圍甚至整個頁面的從新渲 染。一般咱們都沒法預估瀏覽器到底會 reflow 哪一部分的代碼,它們都彼此相互影響着。

 

固然,reflow 問題是能夠優化的,咱們能夠儘可能減小沒必要要的 reflow。好比開頭的例子中的 <img> 圖片載入問題,這其實就是一個能夠避免的 reflow —— 給圖片設置寬度和高度就能夠了。這樣瀏覽器就知道了圖片的佔位面積,在載入圖片前就預留好了位置。

四、repaint(重繪)

另外,有個和 reflow 看上去差很少的術語:repaint,中文叫重繪。若是隻是改變某個元素的背景色、文 字顏色、邊框顏色等等不影響它周圍或內部佈局的屬性,將只會引發瀏覽器 repaint。repaint 的速度明顯快於 reflow(在IE下須要換一下說法,reflow 要比 repaint 更緩慢)。

 

學習了上面的知識後,相信你們對瀏覽器渲染已經有了一個比較深入的瞭解了吧,接下來讓咱們進行更深刻的學習

五、瀏覽器工做大體流程

從上面這個圖中,咱們能夠看到那麼幾個事:

  1)瀏覽器會解析三個東西:

  • 一個是 HTML/SVG/XHTML,事實上,Webkit 有三個 C++ 的類對應這三類文檔。解析這三種文件會產生一個 DOM Tree。
  • CSS,解析 CSS 會產生 CSS 規則樹。
  • Javascript,腳本,主要是經過 DOM API 和 CSSOM API 來操做 DOM Tree 和 CSS Rule Tree.

  2)解析完成後,瀏覽器引擎會經過 DOM Tree 和 CSS Rule Tree 來構造 Rendering Tree。注意:

  • Rendering Tree 渲染樹並不等同於 DOM 樹,由於一些像 Header 或 display:none 的東西就不必放在渲染樹中了。
  • CSS 的 Rule Tree 主要是爲了完成匹配並把 CSS Rule 附加上 Rendering Tree 上的每一個 Element。也就是 DOM 結點。也就是所謂的 Frame。
  • 而後,計算每一個 Frame(也就是每一個 Element)的位置,這又叫 layout 和 reflow 過程。

  3)最後經過調用操做系統 Native GUI 的 API 繪製。

六、DOM解析

HTML 的 DOM Tree 解析以下:

上面這段 HTML 會解析成這樣:

下面是另外一個有 SVG 標籤的狀況:

七、CSS 解析

CSS 的解析大概是下面這個樣子(下面主要說的是 Gecko 也就是 Firefox 的玩法),假設咱們有下面的 HTML 文檔:

因而 DOM Tree 是這個樣子:

而後咱們的 CSS 文檔是這樣的:

因而咱們的 CSS Rule Tree 會是這個樣子:

注意,圖中的第 4 條規則出現了兩次,一次是獨立的,一次是在規則 3 的子結點。因此,咱們能夠知道,創建 CSS Rule Tree 是須要比照着 DOM Tree 來的。CSS 匹配 DOM Tree 主要是從右到左解析 CSS 的 Selector,好多人覺得這個事會比較快,其實並不必定。關鍵還看咱們的 CSS 的 Selector 怎麼寫了。

注意:CSS 匹配 HTML 元素是一個至關複雜和有性能問題的事情。因此,你就會在N多地方看到不少人都告訴你,DOM 樹要小,CSS 儘可能用 id 和 class,千萬不要過渡層疊下去,……

經過這兩個樹,咱們能夠獲得一個叫 Style Context Tree,也就是下面這樣(把 CSS Rule 結點 Attach 到 DOM Tree 上):

因此,Firefox 基本上來講是經過 CSS 解析生成 CSS Rule Tree,而後,經過比對 DOM 生成 Style Context Tree,而後 Firefox 經過把 Style Context Tree 和其 Render Tree(Frame Tree)關聯上,就完成了。注意:Render Tree 會把一些不可見的結點去除掉。而 Firefox 中所謂的 Frame 就是一個 DOM 結點,不要被其名字所迷惑了

 

八、重說渲染

渲染的流程基本上以下(黃色的四個步驟):

(1)計算 CSS 樣式

(2)構建 Render Tree

(3)Layout – 定位座標和大小,是否換行,各類 position, overflow, z-index 屬性 ……

(4)正式開畫

注意:上圖流程中有不少鏈接線,這表示了 Javascript 動態修改了 DOM 屬性或是 CSS 屬性會致使從新 Layout,有些改變不會,就是那些指到天上的箭頭,好比,修改後的 CSS rule 沒有被匹配到,等。

  這裏從新說下兩個概念,一個是 Reflow,另外一個是 Repaint。這兩個不是一回事。

  • Repaint——屏幕的一部分要重畫,好比某個 CSS 的背景色變了。可是元素的幾何尺寸沒有變。
  • Reflow——意味着元件的幾何尺寸變了,咱們須要從新驗證並計算 Render Tree。是 Render Tree 的一部分或所有發生了變化。這就是 Reflow,或是 Layout。(HTML 使用的是 flow based layout,也就是流式佈局,因此,若是某元件的幾何尺寸發生了變化,須要從新佈局,也就叫 reflow)reflow 會從 <html> 這個 root frame 開始遞歸往下,依次計算全部的結點幾何尺寸和位置,在 reflow 過程當中,可能會增長一些 frame,好比一個文本字符串必需被包裝起來。
  • 因此,下面這些動做有很大可能會是成本比較高的。

    • 當你增長、刪除、修改 DOM 結點時,會致使 Reflow 或 Repaint。
    • 當你移動 DOM 的位置,或是搞個動畫的時候。
    • 當你修改 CSS 樣式的時候。
    • 當你 Resize 窗口的時候(移動端沒有這個問題),或是滾動的時候。
    • 當你修改網頁的默認字體時。

      注:display:none 會觸發 reflow,而 visibility:hidden 只會觸發 repaint,由於沒有發現位置變化。

 

九、一些優化建議

(1)不要一條一條地修改 DOM 的樣式。與其這樣,還不如預先定義好 css 的 class,而後修改 DOM 的 className

 

(2)把 DOM 離線後修改。如:

  • 使用 documentFragment 對象在內存裏操做 DOM。
  • 先把 DOM 給 display:none (有一次 repaint),而後你想怎麼改就怎麼改。好比修改 100 次,而後再把他顯示出來。
  • clone 一個 DOM 結點到內存裏,而後想怎麼改就怎麼改,改完後,和在線的那個的交換一下。

(3)不要把 DOM 結點的屬性值放在一個循環裏當成循環裏的變量。否則這會致使大量地讀寫這個結點的屬性。

(4)儘量的修改層級比較低的 DOM。固然,改變層級比較底的 DOM 有可能會形成大面積的 reflow,可是也可能影響範圍很小。

(5)爲動畫的 HTML 元件使用 fixed 或 absoult 的 position,那麼修改他們的 CSS 是不會 reflow 的。

(6)千萬不要使用 table 佈局。由於可能很小的一個小改動會形成整個 table 的從新佈局。

相關文章
相關標籤/搜索