How browser rendering works? –瀏覽器渲染流程解析

Weiwei
Jan 14, 2024

--

Photo by Sandy Millar on Unsplash

不管是不是網頁工程師的你/妳,是否曾經好奇:瀏覽器到底是怎麼把畫面渲染出來的呢?畫面的排列、色彩、元素到底是怎麼被創造出來的?

其實瀏覽器渲染的過程,就像是在蓋一棟房子,不過在蓋房子之前,讓我們先來複習一下前端工程師99.99999%在面試時都會被問到的那題:

在輸入網址後會發生什麼事呢?

首先,瀏覽器會進行DNS的解析,進而找到IP位址

接下來,會進行TCP的三次Handshake,也就是Client端向Server端發送請求詢問是否可以連線,接著Server端回應是否同意,Client端再針對Server端的回應進行響應

接著Client端會向Server端發送Http請求(GET, POST, PUT, DELETE)以取得網站的資料

Server端在接到請求後,返回給Client端網站的資料,例如HTML, JS, CSS, image, ttf, mp4等

前端在接到這些資源後做的事情,就是我們今天要講的重點了!瀏覽器怎麼透過這些資源渲染出畫面呢?接下來就要開始蓋房子了~

用來蓋房子的建築結構藍圖

可以想像我們拿到的HTML檔案其實就是一個建築結構圖,我們要蓋的房子都是依據這個HTML所解析而來

瀏覽器會根據這個html由上到下一行一行解析下來,字節、字符、Token、節點,最後解析成DOM(Document Object Model),組成一棵DOM Tree。

一行一行解析下來時,遇到了引入css檔案的link tag,瀏覽系會先去解析這個css檔案,也一樣會經過字節等流程解析轉換為CSSOM(CSS Object Model),最終組成一棵CSSOM Tree。

接下來DOM Tree跟CSSOM Tree會結合成Render Tree,上面記錄了html element以及其該有的style

不過Render Tree可不只是單純合併DOM Tree跟CSSOM Tree這麼簡單,它還有做了許多篩選,可以想像Render Tree上的東西就是「真的會被Render到畫面上」的元素,所以像是meta tag的內容、display:none的內容等,就是不會在Render Tree上面的。這邊可以注意到,visability:hidden的元素還是會出現在Render Tree上哦,他不像display:none是直接把整個元素拿掉,而是元素還在只是被隱藏了。

好了,現在我們有了房子的鋼骨材料(DOM),也有了它的樣式與顏色(CSSOM),我們要怎麼知道實際哪邊要蓋什麼呢?客廳要蓋在哪?廚房要蓋在哪?

因此接下來瀏覽器會進行Layout的步驟,依照前面拿到的資料對元素進行畫面編排,考量到螢幕的尺寸,元素的長度、寬度、位置等,並且再形成一棵記載有這些資訊的Layout Tree。

這樣講可能有點抽象,下方有個Layout visualization的影片,可以更具體地看出這個步驟時瀏覽器做的事情!

在Layout完成後,就要開始繪製Paint的動作,在這個步驟中會去判斷什麼東西要畫在前面什麼東西要畫在後面等,像下方這張圖的例子,透過z-index去判斷doge神煩狗跟太陽誰在前面誰在後面,不然神煩狗的臉可能就被太陽擋住了!

這邊要再提一下Rastering(柵格化)Compositing(合成)的概念,其實瀏覽器會再根據Layout Tree去生成Layer Tree,去將畫面做分層,就像Photoshop最後的成品是由多個圖層組合而成一樣,在網頁畫面中也會需要三維空間的排列

Rastering(柵格化)

柵格化把獲得的頁面資訊轉為pixels顯示在螢幕上,而早期瀏覽器是都柵格化在viewport中的畫面,如果使用者滑動了,再柵格化新的內容。雖然只處理viewport的部份似乎聽起來滿符合效率的,但其實如果頻繁滑動頁面或者頁面上的資訊/圖片很複雜,每次柵格化都要耗費不少時間與資源。

早期瀏覽器的柵格化方式

Compositing(合成)

也因此,隨著瀏覽器技術的進步,現在採用的是效能更好的Compositing(合成)方式,它的概念是將各個圖層都做好柵格化,並排列組合成目前viewport要呈現的樣貌,當使用者滑動頁面或者有其他動作造成畫面需要改面,再透過將這些圖層排列組合成一個新的viewport frame就好,不需要一直重新柵格化,效能也更優化了。

效能更好的合成方式

接下來講一下Reflow跟Repaint的概念,當我們針對畫面做一些操作,例如點擊某個按鈕、縮放瀏覽器等,畫面有了改變,這時候就會觸發Reflow或Repaint,Reflow就是重新排列組合(Layout),而Repaint則是重新繪製。

Reflow觸發時機:browser大小改變(Resize)、元素尺寸或位置改變、元素內容文字或圖片變化、新增或刪除可見的DOM、啟用CSS虛擬元素(e.g. hover)

Repaint觸發時機:更改背景顏色、更改元素顏色

可以注意到的是,Reflow是一定會一併觸發Repaint的(將磚頭重新排列組合後,一定是需要重新粉刷油漆的)

而Repaint則不一定會觸發Reflow(只是重新粉刷房子,不需要將磚頭重新排列組合)

看到這裡,不曉得你心中有沒有一個疑問:修但幾咧…..講了這麼多,阿操控前端的Javascript大大呢?怎麼半句都沒提到他??

這邊就讓我們來探討瀏覽器在Render時Javascript載入的位置可能會造成的影響:

範例一:

在這個範例中,一行一行解析html時,先開始解析DOM,接著馬上遇到了link載入CSS,瀏覽器會先停下DOM的解析,去解析CSSOM,解析完後,往下又馬上遇到了script tag,因為JS對DOM跟CSSOM都是很重要的(JS可以生成DOM,也有可能操作style),所以瀏覽器又會先繼續解析Javascript,等到完成後,才會再繼續解析與生成DOM,再進行接下來的步驟,也因此可以發現JS的位置可能會拖到DOM的解析,如果JS很大一包,那可能會影響畫面render的效率。

範例二:

在這個範例中,跟第一個範例不一樣的是,JS被放在body close tag的前面,所以在這邊會是,DOM跟CSSOM都解析完後,才去解析JS,也因此畫面的Render較不會受JS載入的因素影響

小結:

希望透過蓋房子的例子能讓大家更了解瀏覽器渲染的流程,而相信大家在了解整個Browser Rendering蓋房子的過程後,會發現到其實瀏覽器在繪製畫面其實做了非常多的事情,也因此要優化瀏覽器渲染的優化,可以思考下面幾點:

  • 盡量減少對DOM的直接操作
  • 減少觸發Reflow, Repaint的次數(例如display:none與visibility:hidden的使用差異)
  • 思考script標籤放置的位置,可以善用defer, async等功能
  • 善用devTool的performance來進行渲染的分析優化

--

--

Weiwei

Front-End Developer|熱愛學習與分享、熱愛甜點的軟體工程師!