Udacity上的 bowser render optimization課程,透過chrome dev tool的timeline來debug,試著將瀏覽器的渲染速度達到60fps。

Frame


如果要在瀏覽透過60fps的速度去渲染畫面

也就是說我們必須在1000ms/60frame ~ 16ms

16毫秒以內將要渲染在畫面上的設定都處理完成

而實際上大約是只有10~12ms

Render Tree


瀏覽器先向server發出http request

接者會收到server傳來的html,css檔案

透過解讀html裡面的tag去建立DOM tree

再來透過css file, inline style, 3rd party css lib去建立CSS tree

並將兩者合併變成Render Tree

Render Tree 就不包含html裡面的head tag

也不會有任何script

另外若Css有對Dom Tree的元素作一些操作

例如

1
2
3
p {
display: none;
}

在確定每個元素的規則樣式之後

接著就是計算每個DOM元素最終在銀幕上的大小和位置

也就是Layout(佈局)

再來就是Paint,將文字、顏色或者圖形等等的繪製在多個不同的圖層上

並透過最後的Composite來做合併

簡化成下圖來做表示整個渲染階段的動作

因此在做60fps優化的時候

我們可以透過chrome dev tool提供的timeline去看這些渲染階段所佔用的時間

另外csstriggers提供了csstrigger的查詢

在做畫面優化的時候可以了解到哪些操作所產生的步驟比較少

進而去提高畫面的表現

App lifecycle


在做優化之前我們應該要先知道

我們有哪些時間上的限制

和使用者對於一個Web app的期待和反應

Action Times Description
R(Response) 100ms 使用者和網頁做互動時的反饋時間
A(Animate) 16ms (actual 10~12ms) 我們期望的60fps的網頁效能
I(Idle) 50ms 使用者看到畫面並且稍微瀏覽的時間
L(Load) 1000ms 使用者送出request到畫面出現的時間

在了解到App lifecycle之後

我們就可以針對下面的流程來做優化拉!

Optimizing JS


Optimizing JS for Animations

不要去做 Micro-optimization

像是去比較for-loop跟while-loop的差別

可以透過timeline tool

看到哪個function導致執行時間變長

Web Wokers

有時候我們會透過javascript去處理一些運算較為久的task

並在算完之後渲染到畫面上

但由於js engine是單執行緒

在處理這些task的時候可能會導致渲染的效能變慢

這時候就可以透過web worker建立一個背景的執行緒

幫我們完成這些work

透過這段code來宣告一個新的web worker

main.js
1
var myWorker = new Worker("worker.js");

接著我們就可以在worker.js裡面去處理我們要執行的task

main script和web worker主要透過 postMessage() 方法以及 onmessage 事件處理器來溝通

要注意的是在worker.js中必須用self來指定全域物件

因為此時的worker不是在window這個scope底下

worker.js
1
2
3
4
5
6
7
8
9
10
11
12
13
importScripts('otherScripts.js');
self.onmessage = function(e) {
var data = e.data;
try {
/* heavy work here */
}
postMessage(result);
} catch (e) {
/* handle error */
postMessage(undefined);
}
}

透過worker處理完的data我們就可以在main script當中透過onmessage來監聽

main.js
1
2
3
4
/* ... */
myWorker.onmessage = function(result) {
/* handle result */
}

如此一來就可以把較為耗資源的運算跟渲染的運算做分離

避免渲染時卡卡的情形發生了

MDN-Web Workers

JS Memory Management

Optimizing Style & Layout Calculation


Style change的cost是和change的element數量成線性的關係

因此我們會希望每次做style change的時候改變的element數量越少越好

藉此加快 Style Caluculation的速度

Selector Matching

Block Element Modifier

再透過class改變style的時候

我們會希望影響到的element越少越好

並且在selector的地方越簡單越好

Stopping F(orced) S(ynchronous) L(ayout) Strategy

千萬不要改變渲染的順序否則會造成很差的perf

JS -> Style -> Layout -> Paint -> Composite

Optimizing Paint & Composite Calculation


Paint Profiler

透過timeline的Paint

去記錄整個paint的過程

甚至可以直接看到在對應的function call時

所完成的paint

Composite

越多Layer Composite time越久

所以當我們透過增加Layer去減少Paint次數時

Composite的時間就會變長

Manage Layers

1
2
3
4
5
6
7
8
9
10
.circle {
will-change: transform;
}
*newer browser
.circle {
transform: transformZ(0);
}
*older browser

可以讓有will-change或transform的component

建立自己的Layer

demo will-change

試著把.box加上will-change: transform

渲染效果差超級多

Layers Counting

透過Timeline的Paint Profiler

我們可以直接點擊Layers看到頁面有多少層Layer

並且可以透過圖像化的表示將Layer做旋轉

看到不同Layers的相對位置