前端工程師應該要懂的瀏覽器渲染機制

前端工程師應該要懂的瀏覽器渲染機制

前言

最近剛好有學生詢問這個議題,剛好這個議題我覺得對於一名前端工程師來講是滿重要該懂的東西,畢竟我們身為前端工程師時常在跟瀏覽器打交道,不好好認識它的運作機制怎麼可以呢?

瀏覽器渲染機制

首先我必須先說一件事情

『瀏覽器本身就是一個黑魔法』

這句話的意思是說,瀏覽器的運作機制其實是非常複雜的,如果用比較單純的方向去思考,我們的思考點可能會是這樣

  1. 使用者跟伺服器請求了一個 HTML 檔案
  2. 瀏覽器解析 HTML 檔案
  3. 下載 CSS 檔案
  4. 下載 JS 檔案
  5. 下載圖片檔案
  6. 瀏覽器將 HTML、CSS、JS、圖片等檔案組合成一個網頁
  7. 瀏覽器將組合好的網頁顯示在畫面上

簡單一點的思考方式確實是這樣,但其實瀏覽器的渲染與運作比我們想的還要更複雜。

其實瀏覽器都是多執行緒

如果你是一名前端工程師,基本上應該會一直聽到人家說 JavaScript 是一個單執行緒的語言,然後你可能會想說瀏覽器是 JavaScript 的執行環境,那瀏覽器應該也是單執行緒的吧?

答案是錯的,因為瀏覽器其實本質是多執行緒的,這也是為什麼我們在寫 JavaScript 的時候會有非同步的概念,因為瀏覽器會將一些工作丟到其他執行緒去做,然後等到完成之後再回來通知主執行緒。

那為什麼會特別提到這個呢?因為瀏覽器的運作機制中,有包含以下五大類:

  • Network Process
  • Render Process
  • Plugin Process
  • GPU Process
  • Browser Process

這邊也額外提一下 Network Process 跟 GPU Process 是從 Browser Process 中分出來的,因為這兩個工作是非常耗時的,所以才將這兩個執行緒分出來,讓 Browser Process 可以專心處理其他的工作。

雖然我們今天要著重於介紹 Render Process,但還是要稍微提一下這些執行緒的工作內容,讓大家對於瀏覽器的運作機制有更深入的了解。

  • Network Process:負責處理網路請求,例如:下載 HTML、CSS、JS、圖片等檔案,這個執行緒會將請求的檔案下載回來,然後傳給 Browser Process
  • GPU Process:負責處理 GPU 的工作,例如:將網頁渲染到畫面上,這個執行緒會將 Render Process 的工作結果傳給 GPU,然後讓 GPU 將網頁渲染到畫面上,透過 GPU 的加速來提高效能
  • Plugin Process:負責處理早期的外掛模組(如 Flash、Java Applet 等),雖然現代瀏覽器已大多不再使用,但部分架構上仍保留此 Process 作為兼容機制。

接下來就來介紹 Render Process 吧!

Render Process

我們在請求一個網頁回來時,瀏覽器會進行一系列的處理,這些流程包括以下:

  1. HTML 解析:將下載請求(Request)回來的 HTML 檔案進行解析,這個裡面通常包含了 HTML 標籤、CSS 標籤、JS 標籤、圖片標籤等等
  2. DOM 樹建立:將解析出來的 HTML 檔案轉換成 DOM 樹,也就是我們所謂的 Document Object Model,這個樹狀結構會包含所有的 HTML 標籤與屬性
  3. CSS 樹建立:將下載請求回來的 CSS 檔案進行解析,解析完成的 CSS 會被轉換成 CSSOM(CSS Object Model)
  4. Render Tree 建立:將 DOM 樹與 CSS 樹進行結合,產生 Render Tree
  5. Layout:計算 Render Tree 中每個節點的大小與位置
  6. Page Paint:將 Render Tree 中的每個節點進行繪製
  7. Composite:將不同圖層合成一個畫面,這個階段會由 GPU 來加速處理,所以其實你瀏覽器畫面上所看到的一切都是一層一層疊上去的圖層
  8. JavaScript 執行:執行 JavaScript 程式碼,並更新 DOM 樹與 CSS 樹

它們的流程會是這樣的:

1
靜態資源 → DOM 樹 → CSS 樹 → Render Tree → Layout → Page Paint → Composite

接下來當每一次發生 DOM 或 CSS 更新時,就會執行兩個動作,也就是所謂的 Reflow 與 Repaint 的動作,這兩個動作的差別在於:

  • Reflow:重新計算 DOM 樹中每個節點的大小與位置
  • Repaint:新繪製受到樣式變更影響的元素(例如:背景色、邊框、陰影、顏色等),但不會重新排版。

這兩個觸發時機點是何時呢?

只要跟 DOM 結構或樣式有關的變化,都可能會觸發 Reflow 與 Repaint 的動作,而當觸發這個動作時,就會讓整個機制從頭開始跑,這也是為什麼人家說 DOM 的操作成本很大。

那麼什麼時機點會觸發 Reflow 跟 Repaint 呢?

基本上如果你修改的是 DOM 結構,那就會觸發 Reflow 與 Repaint 的動作,但如果只是修改樣式或屬性,那就只會觸發 Repaint,如果想要提高性能,就需要謹慎操作 DOM。

Virtual DOM 解決了什麼?

最後來提一下 Virtual DOM,只要你是一名前端工程師,那就肯定會接觸到一些框架,這些框架為了提高效能以及方便 JavaScript 的操作,就會使用 Virtual DOM 的概念。

這個概念的核心在於,當我們對 DOM 進行操作時,實際上是先在一個虛擬的 DOM 上進行變更,然後再將這些變更批次更新到真實的 DOM 上,並不是馬上更新到真實的 DOM 上,這可以有效減少 DOM 操作的成本。

那…Virtual DOM 有提高效能嗎?

事實上來講並沒有,反而增加了一些額外的計算成本,畢竟 Virtual DOM 是將原本的 DOM 轉換成 JavaScript 物件,接著當我們要更新 DOM 時,會先將 Virtual DOM 更新,然後再將 Virtual DOM 與真實的 DOM 進行比對,最後再將差異的部分更新到真實的 DOM 上,之所以 Virtual DOM 看起來有提高效能的錯覺是因為裡面有一些優化的機制,如:Diff Algorithm、Batch Update 等等,這些優化的機制可以有效減少 DOM 操作的成本,但實際上 Virtual DOM 並沒有提高效能,只是將原本的 DOM 操作變得更簡單而已哩。