前端瀏覽器渲染優化

By Jordan Irabor | January 17 2019

原文css

介紹

咱們生活在一個強調快速提供網絡服務的時代。因爲web應用的負載傳輸不斷增長,開發者必須採起最佳實踐來確保數據包被即刻分發,以此提供給用戶拉稀般的網絡體驗html

現在被web開發普遍採用的最佳實踐是圖片壓縮、代碼壓縮、代碼打包(webpack打包)等等。這些實踐已經被證實了能有效提升用戶體驗,若是開發者可以深刻理解瀏覽器渲染原理的話,還能夠優化性能以提升用戶體驗。webpack

當咱們在低端機子玩對GPU要求高的遊戲的時候,咱們會遇到一些卡頓——環境和遊戲人物的卡頓。這種現象(雖然不那麼明顯)也會在web應用上出現;用戶會在進行鼠標點擊或滾動頁面等的頁面交互時感覺到應用會有一兩秒的卡頓,從而會中斷用戶的交互操做web

在本文中,咱們將討論Web應用程序以每秒60幀的速度運行(最佳)的條件chrome

什麼進入了頁面

在web應用上,每當頁面發生變化,在瀏覽器內部發生的事情是這樣的:瀏覽器爲用戶提供給了一個新的頁面,以便用戶瀏覽和交互。 這些幀出現(而且更新)的速率以每秒幀數(fps)來度量。若是瀏覽器花了太長時間來建立和渲染一張頁面,fps就會降低,用戶就會感受卡頓瀏覽器

爲了讓瀏覽器穩定在60幀,開發者須要理解頁面的渲染。下面是頁面建立的五個步驟:服務器

  1. 瀏覽器發起get請求網絡

  2. 服務器返回html、cssfrontend

  3. html被解析成domdom

  4. css被解析成css對象模型(cssom)並與dom整合成渲染樹

  5. 渲染樹基本上由頁面上顯示的元素組成,並構成一個頁面。

web應用的生命週期

在咱們探索瀏覽器渲染路徑並優化以前,咱們須要學習應用的生命週期,由於這可使咱們作出明智的選擇,以肯定應用程序什麼時候應該執行「繁重的工做」。 從而創造流暢的用戶體驗並加強用戶滿意度。

應用生命週期被分離出四個步驟:

1.Load

2.Idle

3.Animation

4.Response

Load

在用戶操做web應用以前,web應用就已經被加載好了。 這是應用生命週期的第一步而且最重要的是這一步旨在將負載時間減小(理想狀況下爲1s)到儘量小的數量。

Idle

應用加載完以後,常常是空閒狀態;等待用戶操做。應用加載完後到用戶操做以前的這段空閒時間通常是50ms,開發者能夠用這段時間坐一些耗時的工做, 例如加載一些用戶很快要用到的資源(圖片,視頻)。

專業提示:顯着減小加載時間的一個方法是首先加載UI的基礎節點,而後在空閒階段引入其餘元素。

Animate

當用戶開始操做應用而且空閒期已經結束時,應用程序必須對用戶交互(和輸入)作出適當的反應而沒有任何明顯的延遲。

提示:研究代表,須要大約十分之一秒(在與UI元素交互以後)才注意到卡頓。 所以,在此時間範圍內響應用戶輸入是完美的。

當對用戶交互的響應涉及某種動畫時,可能會有一些挑戰。爲了將動畫穩定到60幀每秒,每幀的限制爲16ms - 這基本上是一秒除以60。

那麼,瀏覽器的開銷就應該時10ms到12ms。 實現此目的的一種方法是預先執行全部動畫計算(在UI元素與之交互後的100ms內)。

瀏覽器渲染路徑和優化空間

瀏覽器渲染路徑有如下幾個步驟:

1.js 2.樣式計算 3.佈局 4.繪製 5.合成層(Layer composition)

在網頁上,頁面發生改變(要麼css要麼js致使的),瀏覽器從新計算受影響元素的樣式。若是元素的位置、形狀等影響佈局的地方發生更改, 瀏覽器將檢查其餘元素,建立新佈局,從新繪製受影響的元素並將這些元素從新組合在一塊兒。

可是,更改頁面元素的某些屬性可能會更改網頁的渲染路徑。例如,若是是繪畫相關的屬性,好比背景圖片或者文字顏色這些有變更, 佈局不會受影響由於這沒有改變屬性的位置和形狀。其餘屬性更改可能會使佈局生成和繪製脫離渲染管道。

咱們將會展現一些關於瀏覽器渲染的一些優化。

js

js讓開發者爲用戶提供優秀的動畫效果和視覺體驗,所以js成爲web應用中必不可少的一部分。咱們在上文對應用生命週期的討論中提到, 瀏覽器有10ms~12ms去渲染每個頁面。爲了減輕js在渲染管道的負擔(譯者:由於js執行時間過長致使一頁面的渲染超過12ms), 在每一個頁面(幀)中儘量早地執行全部JavaScript代碼很是重要,由於它可能會觸發渲染管道的其餘區域。

使用 window.requesAnimationFrame() 方法頗有必要,詳情請看MDN文檔

window.requestAnimationFrame()方法告訴瀏覽器您但願執行動畫並請求瀏覽器調用指定的函數以在下一次重繪以前更新動畫。 該方法將回調做爲在重繪以前調用的參數

requestAnimationFrame方法使得瀏覽器能在正確的時機引入瀏覽器而且防止掉幀。下面是一個例子:

function doAnimation() {
    // Some code wizardry
    requestAnimationFrame(doAnimation); //schedule the next frame

}

requestAnimationFrame(doAnimation);
複製代碼

在google開發者工具裏的performance一欄中,容許開發者去記錄每一頁面(幀)繪製出來的狀況,能夠看到js在web應用中執行的狀況

雖然requestAnimationFrame方法是很是重要的工具,可是仍是會有些js代碼很是耗費資源。 網站在咱們的操做系統的主線程上運行,所以這些腳本可能會中止渲染管道的其餘階段的執行。要解決這個問題,咱們可使用Web worker。

web worker 能讓咱們在新的線程執行那些耗費資源的js代碼。詳情請看MDN文檔

「有了web worker就簡單了,咱們能夠把耗時的工做丟給後臺進程。worker進程能夠在不干擾用戶界面的狀況下執行任務。 一旦被建立,worker能夠經過將消息發佈到由該代碼指定的事件處理程序來向建立它的JavaScript代碼發送消息(反之亦然)。」

要使用此功能,你須要建立一個單獨的JavaScript文件,你的主程序將生成一個Web worker。

樣式計算

樣式更改是任何Web應用程序渲染管道的關鍵部分,由於其元素所需的樣式更改數量與樣式從新計算的性能成本成正比。 能夠查看Paul’s website來了解css樣式影響渲染管道的細節。

除了樣式變動的數量,選擇器匹配應該考慮到咱們的渲染優化列表中。選擇器匹配是指肯定應將哪些樣式應用於任何給定DOM元素的過程。

某些樣式可能比其餘樣式須要更多時間來處理,而且時間隨着受一個或多個樣式更改影響的元素數量的增長而增長。 解決此問題的合適方法是塊元素修改器(BEM)方法。它提供了很好的性能優點,由於類匹配符合BEM方法,是現代瀏覽器中最快的選擇器。

佈局

一個主要的性能瓶頸是佈局抖動。在js從新計算元素節點的位置和形狀時,會致使瀏覽器從新佈局。這樣,當快速連續幾回完成時,會致使強制同步佈局。 在這篇文章中, Google的Paul Lewis,強調了能夠採起的各類優化措施,以防止強制同步佈局。

繪製

瀏覽器在開始填充屏幕像素時發生繪畫。這包括在屏幕上繪製全部視覺元素(譯者注:即在渲染樹上的節點都會被繪製,無論它在不在屏幕視窗內), 這會生成多個平面,叫圖層。當須要大面積繪製時,尤爲是頁面滾動時,繪製一樣會致使性能問題。

繪製分析器,如上圖。這讓咱們很容易就知道頁面的哪些區域被繪製和何時繪製的。

第一個多選框,Paint flashing,勾上以後頁面會以綠色高亮被從新繪製的部分。 這個頻率能夠告訴咱們繪畫是否會影響渲染管道上的性能問題。

咱們看看上面這張圖,能夠看到在頁面滾動時只有滾動條被從新繪製了。代表Scotch.io網站的主頁上有作瀏覽器繪製的優化。

合成層(Layer Composition)

這是瀏覽器渲染管道的最後一步,它包含了重要的瀏覽器結構-圖層(Layers)。瀏覽器引擎經過首先考慮樣式和元素以及它們的排序方式來進行一些層管理。 而後嘗試找出頁面所需的圖層並相應地更新圖層樹。

接下來,瀏覽器合成這些圖層並在屏幕上顯示它們。 當瀏覽器必須繪製彼此重疊的頁面元素而且彼此存在於同一層中時,會出現因爲繪製而致使的性能瓶頸。

要解決此問題,所涉及的元素必須存在於單獨的層中。 這能夠經過改變CSS屬性並將其屬性設置爲transform來實現:

<element_to_promote> {
    will-change: transform;
}
複製代碼

然而,應該注意的是,層的增長意味着花在層管理和合成上的時間增長。 使用Chrome開發工具,能夠查看頁面上的全部圖層,以下所示:

打開chrome開發者工具,點擊hamburger菜單按鈕(三個豎着的點),選擇more Tools,選擇Layers。

相關文章
相關標籤/搜索