Photo by Helloquence on Unsplash

效能從了解瀏覽器開始 —簡易評估並優化渲染路徑

Xuan Li
Walk Out 技術共筆
8 min readSep 28, 2019

--

Fewer Bytes Faster Renders

最直覺優化 Render 方式肯定會想到盡量壓縮檔案的大小,越小的檔案便能減少 request 收到回應的速度,同時也可能減少對伺服器端的請求次數,那麼縮減檔案的方法有哪些呢?再來如果可以不用都重新下載檔案是不是也能提高網站的效能呢?

Minify、Gzip、Http cache 相信都是大家耳聞過的方法或技術,包含圖片壓縮等等,而上述這些方法與技術其後面的原理都值得去探討了解,本文章只會簡易表達一下背後的觀念.

首先是 minify 的部分,在網頁內容中,開發者所需要的內容比對瀏覽器所需要的內容是不一樣的,好比我們的檔案中會包含註解、排版需求上的斷行及空白,但這些內容實際上是不影響頁面呈現的,卻仍舊佔了檔案大小,因此最簡單的方式便是在 production 的檔案中去除不必要的註解以及空白,以及圖片的大小可以透過演算法去壓縮,minify 可以說是最常見的手法

Gzip 則在壓縮文字上有顯著的效果,並且所有瀏覽器都支援並主動請求 Gzip 壓縮,當然此動作需要先在 server side 設定好支援 Gzip 的 Flag,而特別注意若已經過其他演算法或方式去壓縮後的檔案,再套用到 Gzip 上就不會有什麼效果,再來是若檔案過小或已經經過其他壓縮方式後的檔案,是有可能經由 Gzip 後反而得到較大的檔案,透過下面網站

https://www.whatsmyip.org/http-compression-test/

我們可以檢查是否引用 Gzip ,以及比對壓縮後的大小.

再來探討 HTTP Cache,Cache 可以想像遊樂園進門的票證印章,當你手上的印章給工作人員看時,日期若是符合今天,則不必再重新買票便可以再次入場,反之如果手上印章不符合,則必須再次排隊買票並取得新的蓋章.在此範例中,我們代表的便是 request 所想要的檔案,蓋章則是 HTTP Cache 判斷的 Etag token,工作人員則是伺服器,收到請求時,伺服器端會先檢查 Etag token 對比當下資源是否有被修改,若沒有變動則不再重新下載 response,以及判斷此 Etag token 是否已經過期,若過期則不再使用快取中的 response 而是從伺服器端重新抓取新的版本.

其中針對 cache control 有著 no-cache & no-store 的區別,前者必須與伺服器端確認檔案是否變更,若有對應的 Etag token 則不必重新下載檔案;後者則不確認檔案是否變更,直接下載完整檔案.

以上所述只是大概的講解,小總結一下優化的第一步便可以從這三個步驟下手 Minify, Compress, Cache

若大家想深入研究 Gzip 的機制或者 http cache 的驗證機制的話歡迎到下列網站研究研究

Http Cache https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching

壓縮以及Gzip https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer#minification-preprocessing--context-specific-optimizations

講完了第一步驟優化的過程,接著我們第二步主要是針對 Render 的方面來優化,首先我們知道在前一篇文章中提到,CSS 是 Render Blocking,也就是在 CSS 完整下載並解析前是不會進入到 Render 步驟的,但有些 CSS 檔案是針對手機版或者特定螢幕的狀況下所引入的,在支援 RWD 下同一份 HTML 可能一次引入包含 PC & Mobile 的 CSS樣式,也就意味著在手機上你也必須等待 PC 版樣式讀取完畢才能 Render,儘管此樣式在手機顯示上是完全不必要的,此時我們可以透過 media 去告訴瀏覽器這檔案是不影響當前畫面顯示的,也就是不要視為 Render Blocking

<link href="print.css" rel="stylesheet" media="print">
<link href="other.css" rel="stylesheet" media="(min-width: 40em)">

第一個範例告訴了瀏覽器,若非在列印模式下觀看則此檔案不影響畫面呈現,瀏覽器便不會等待此檔案完成後才進入 Render 步驟,同理第二個檔案則告訴瀏覽器螢幕最小寬度 40em 下才需要此樣式,其餘尺寸則不需等待此檔案的解析.特別注意到的是,media 只是告訴瀏覽器哪些檔案不影響 Render 步驟同樣會抓取所有引用檔案

同樣針對渲染來說,接著探討 Javascript 的影響,首先由前一篇文章我們知道操作 DOM 的成本為什麼如此之高,但 JS 不只因此而棘手,更麻煩的是它會中斷 HTML parser

如上圖,注意到當建立 CSSOM 後緊接著的是 JS 接著才繼續完成整個建立 DOM Tree 的行為,也就是 JS 中斷了 DOM 的執行,而當 DOM 被中斷自然而然地整個 Render 過程也會因此花費更多時間等待.

當然有時候利用 JS 操作 DOM 也是無可避免的,而現在常見的 framework 也都透過 Virtual Dom 比對盡量避免不必要的消耗.在這邊介紹兩個屬性可以避免 JS 阻擋 parser 進行,分別是 async & defer,若是明確知道引用的 JS 檔案是無關 DOM 的組成可以加入此兩屬性告訴瀏覽器此檔案不影響 DOM 建構因而不要阻止 parser 解構,就是避免 Parser Blocking.

上面提到的兩點都有一個共同的目標,也就是盡量減少影響 Critical Resource Path 的過程,CSS media 減少 Render Blocking 的可能;JS derfer/Async 減少 Parser Blocking 的可能.而 google 也針對 CRP 的優化列出了三項指標,並以下圖為例

  1. Number of Critical resources - 3 (HTML, CSS, JS)
  2. Total critical KB - 11 KB
  3. Minimum critical path length (roundtrips) - 2 (瀏覽器可以同時下載JS, CSS)

此外瀏覽器有一個機制也值得一提,pre-load scanner,由於每次遇到 JS 時 parser 便會被中斷的行為著實太沒效率,瀏覽器會在 JS 執行時,透過 scanner 掃描剩餘的文檔尋找是否有其他 resources Ex: CSS, js, img … 等等並在 background 先行下載,等 Parser 再次啟動遇到這些檔案時便能省下下載的時間,如下圖的 timing.js

以上就是關於優化關鍵渲染的一些簡易介紹,做個重點整理

  1. 可以事先評估自己網站上的 CRP 三項指標並衡量是否可以優化的部分
  2. 在 CSS 以及 JS 上可以透過 media, defer, async 屬性避免 blocking 時間
  3. Minify, Compress, Cache 三動作減少下載的時間以及送出的請求

以上就是這次的小筆記,有任何錯誤或者建議以及您覺得沒提到但很重要的觀念,歡迎底下留言,如果覺得本文有小小幫助的話希望不要吝嗇啪啪啪的打響我的臉(claps)

Reference :

Udacity course - https://classroom.udacity.com/courses/ud884/lessons/1469569174/concepts/15244185810923

Pre-load scanner - https://andydavies.me/blog/2013/10/22/how-the-browser-pre-loader-makes-pages-load-faster/

CRP 指標 - https://developers.google.com/web/fundamentals/performance/critical-rendering-path/analyzing-crp

--

--

Xuan Li
Walk Out 技術共筆

1. 30歲的前端工程師 2. 身體脂肪抗爭中的運動愛好者 - 健身, 慢跑, 游泳 3. 喜歡閱讀 -> 近期閱讀 自私的基因 4. 喜歡看劇看電影並從中獲得生活啟發 5. 嘗試練習分享中