CSS 常被認為簡單好學,我對它看法是易學但難精通,以前都只想過實現各種 UI、效果,但沒認真考慮過效能,怎樣的寫法最適合這個情境。假設你有針對 google page speed、lighthouse 優化過,應該會特別有感觸,該如何才能讓網頁載入更快。

接下來討論如何讓畫面渲染載入更快,以及一些最佳化使用方式。

網頁渲染邏輯

首先介紹最基礎的 painting,瀏覽器的畫面產生,需要有幾個步驟,先是由上至下掃一遍 html,擷取內容後,先產生 HTML DOM tree,再來產生 CSSOM tree,

經由上面步驟,轉譯樹狀結構,將 兩個 tree 合併起來產生 render tree,最後是關鍵的步驟,根據每個節點,依照 style 決定 layout 版面配置,或是決定樣式套用,並且繪製在畫面上,就是我們看到 div 並帶有 width 等樣式。

html DOM CSSOM
HTMLDOM CSSOM Render Tree (畫圖花了我三十分鐘...)

Google: 轉譯樹狀結構的建構、版面配置和繪製

所謂的優化關鍵路徑,就是要縮短這整個流程的時間,擷取 HTML -> DOM tree -> CSSOM tree -> render tree -> painting。

避免使用影響載入

網頁載入速度快慢影響,首先是資源請求速度,網頁上的 CSS JS file 都會是需要發 http 請求的,遇到這狀況時,解析器都必須先停止解析 HTML 並執行該腳本,然後才能繼續解析。所以盡可能避免過多個資源請求,讓 css 或是 js print 到 html 上。

針對 script file 推薦用 async 或是 defer,async 是不中斷 HTML parser 並當載入 js file 完成立刻執行,defer 則是當整體 HTML parser 完成,再回頭執行 js file。小提醒 async 代表在這邊操作 DOM 都是可能失敗的,因為你無法預知載入 file 時,DOM 有無產生。

async defer
script async defer

神圖介紹來源: whatwg

同時假設在 HTML parser 階段就開始執行 javascript,都會中斷 HTML parser 的,所以這邊要盡量避免在初始化執行大量 javascript,可以用事件監聽 DOM loaded 觸發執行。

另外就是 CSS import font,實務上常常是這點卡住,這建議用 javascript 非同步方式載入,ps. 我們用過這方法大概推進 lighthouse 20 分左右。

至於 http 2,目前還有滿多 cdn 尚未支援的,這邊就不多討論,大概概念就是這可以大幅加快 http 請求速度,實現同時多個 http 請求,並且不阻塞。

最後假設 SPA 是純 client side render 又要追求載入速度的話,直接起跑點就輸了,比較建議考慮直接改 server side render,你用上面邏輯跑一遍,就大概知道這是 飛機 vs 機車的差距了。

心得

我自己是覺得原理滿重要的,當初在學習前端時候,常常只是無腦跟著教學走,只是覺得瀏覽器真神奇,各種炫砲畫面,假設知道原理後,就更容易知道問題在哪裏,其實我原本是想寫 CSS animation 的,沒想到研究研究就覺得這應該也滿重要的,就先寫這篇了。

下方內容滿推薦的,真心推薦前端都需要看過一遍,會對於效能這件事,有更深的了解。

google 教學: 網頁渲染運作邏輯

一樣感謝閱讀,有問題歡迎留