其實精確一點,這篇文章想討論的是 Rendering Performance,也就是網頁在跟使用者互動的過程中,要怎麼樣才能有流暢的體驗。尤其 mobile device 的處理能力比較弱,要在受限制的環境展現良好的使用者體驗又更是個挑戰。

我們先看看在這方面表現優秀的網站 Google I/O 2015,在右上角(手機開啟的話是在左上角)的 Navigation 按按切換玩玩看,你可以發現網站的動畫相當的流暢。網頁內容也都可以點點按按,整個表現都相當不錯。

要達到流暢的網頁使用者體驗,其實是有些技巧的,但要學習這些技巧之前,我們需要先了解瀏覽器是怎麼 Rendering 的。

Pixel Pipeline

Pixel Pipeline Stage
**請把這張圖記在心裡、請把這張圖記在心裡、請把這張圖記在心裡,因為超重要**

Pixel to Screen 會照順序經過五個階段:

  • JavaScript, 這個階段就是你用 JS 去對畫面做出視覺上的改變,舉凡增減元素、使用 jQuery 的 animate 或是排序你的資料都是。因為他們最終都會對視覺做出改變。
  • Style, 在這個階段瀏覽器會根據 CSS selector 判斷 CSS 的 rules 必須要提交到哪些元素。 我們的 CSS 可能會寫

    1
    .header > .navigation > .items

    之類的階層,哪些元素會被哪些規則影響,有什麼樣的 style,就是在這個階段決定。

  • Layout, 瀏覽器知道哪些元素會對應到哪些 style 後,這個階段計算各個元素會在螢幕上佔用多少的空間(e.g. 寬、高),以及在螢幕上的位置等等。注意,每個元素都有可能會影響其他的元素,比如 <body> 的 style,可能會影響他所有的子元素的寬。
  • Paint, 這個階段會將這些 pixel 繪製在多重的、被稱作 layer 的平面上,包含繪製文字、顏色、圖像、陰影等等,其實就是包含了元素所有的視覺(visual)部分
  • Composite, 因為一個頁面瀏覽器會把它切成許多不同的 layer,必須要妥善的處理這些 layer 再照順序把它們繪製到螢幕上。尤其要處理不同 layer 之間 overlap 的情形,瀏覽器必須正確的繪製哪些部分是被蓋住的,哪些部分是必須讓使用者看到要畫出來的。

調校 Performance 的藝術就是避免做事情

保持一個心態,能不做我就不做,這樣就對了(請不要用在工作或是待人處事上)。

1. JS / CSS > Style > Layout > Paint > Composite
假如你更動了所謂的 “layout” 屬性,例如:元素的 width、height、left、top。不論你是用 JS 還是 CSS 的方式去更動,瀏覽器必須要去重新計算各個元素的位置跟大小(就算你只改了一個元素,這個元素也可能影響了其他的元素,所以都要重算),這個行為稱為 Reflow。然後瀏覽器必須去重新繪製被影響的元素,組出畫面來。也就是 Pixel Pipeline 的五個階段都會執行到。

2. JS / CSS > Style > Paint > Composite
假如你只更動了所謂的 “Paint Only” 屬性,比如:元素的 background image、text color 或是 shadow,更動這些特性不會影響頁面的 layout,所以讓瀏覽器可以跳過 Pixel Pipeline 中的 layout 這個步驟。像這樣的行為稱為 repaint。少做一件事,YA!
Pixel Pipeline Stage

3. JS / CSS > Style > Composite
更甚者,某些行為不會啟動瀏覽器的 reflow,也不會啟動 repaint,它只會執行 compositing。這是 performance 最好,也是我們的終極目標,可以用在解決網頁的捲動或是動畫這樣高壓力或是說最容易出現卡頓(jank)的情況。
Pixel Pipeline Stage

現在只有兩種屬性可以達成這個目標,就是 transformopacity。如果可能用 transform 跟 opacity 來達到一樣的效果,就應該要用。因為這兩個屬性只會觸發 composite。

Composite-Only

更甚者,我們還可以對你計畫要動畫的元素,去 promote 它,為它新增一個 compositor layer。透過如下的方式:

1
2
3
.moving-element {
will-change: transform;
}

對於不支援 will-change 的瀏覽器,使用:

1
2
3
.moving-element {
transform: translateZ(0);
}

這樣透過新增 compositor layer 的方式可以讓動畫運行的更順暢,因為瀏覽器支援同時繪製 multiple layers。但新增 layer 也意味著多消耗記憶體,所以要小心的使用這個 promote 的功能。

為什麼這些很重要

因為現在大部分的機器一秒更新 screen 60次,也就是說一個 frame 約 16ms (1 secode / 60 = 16.66ms)。如果 Pixel Pipeline 執行一次超過 16ms 通常就會造成畫面的卡頓,然而除去瀏覽器自己的工作,我們必須把我們能控制的計算(process time)壓在 10ms 以內才是比較理想的狀況。

進階閱讀

在上述的 Pixel Pipeline 裡,每個階段都有可以 optimize 的地方,之後會再發文章說明。但等不及的話可以先看看:

Reference

All photos in this article credit to Web Fundamentals