前端浏览器渲染优化
By Jordan Irabor | January 17 2019
原文
介紹
我們生活在一個(gè)強(qiáng)調(diào)快速提供網(wǎng)絡(luò)服務(wù)的時(shí)代。由于web應(yīng)用的負(fù)載傳輸不斷增加,開(kāi)發(fā)者必須采取最佳實(shí)踐來(lái)確保數(shù)據(jù)包被即刻分發(fā),以此提供給用戶拉稀般的網(wǎng)絡(luò)體驗(yàn)
如今被web開(kāi)發(fā)廣泛采用的最佳實(shí)踐是圖片壓縮、代碼壓縮、代碼打包(webpack打包)等等。這些實(shí)踐已經(jīng)被證明了能有效提高用戶體驗(yàn),如果開(kāi)發(fā)者能夠深入理解瀏覽器渲染原理的話,還可以優(yōu)化性能以提高用戶體驗(yàn)。
當(dāng)我們?cè)诘投藱C(jī)子玩對(duì)GPU要求高的游戲的時(shí)候,我們會(huì)遇到一些卡頓——環(huán)境和游戲人物的卡頓。這種現(xiàn)象(雖然不那么明顯)也會(huì)在web應(yīng)用上出現(xiàn);用戶會(huì)在進(jìn)行鼠標(biāo)點(diǎn)擊或滾動(dòng)頁(yè)面等的頁(yè)面交互時(shí)感受到應(yīng)用會(huì)有一兩秒的卡頓,從而會(huì)中斷用戶的交互操作
在本文中,我們將討論Web應(yīng)用程序以每秒60幀的速度運(yùn)行(最佳)的條件
什么進(jìn)入了頁(yè)面
在web應(yīng)用上,每當(dāng)頁(yè)面發(fā)生變化,在瀏覽器內(nèi)部發(fā)生的事情是這樣的:瀏覽器為用戶提供給了一個(gè)新的頁(yè)面,以便用戶瀏覽和交互。 這些幀出現(xiàn)(并且更新)的速率以每秒幀數(shù)(fps)來(lái)度量。如果瀏覽器花了太長(zhǎng)時(shí)間來(lái)創(chuàng)建和渲染一張頁(yè)面,fps就會(huì)下降,用戶就會(huì)感覺(jué)卡頓
為了讓瀏覽器穩(wěn)定在60幀,開(kāi)發(fā)者需要理解頁(yè)面的渲染。下面是頁(yè)面創(chuàng)建的五個(gè)步驟:
瀏覽器發(fā)起get請(qǐng)求
服務(wù)器返回html、css
html被解析成dom
css被解析成css對(duì)象模型(cssom)并與dom整合成渲染樹(shù)
渲染樹(shù)基本上由頁(yè)面上顯示的元素組成,并構(gòu)成一個(gè)頁(yè)面。
web應(yīng)用的生命周期
在我們探索瀏覽器渲染路徑并優(yōu)化之前,我們需要學(xué)習(xí)應(yīng)用的生命周期,因?yàn)檫@可以使我們做出明智的選擇,以確定應(yīng)用程序何時(shí)應(yīng)該執(zhí)行“繁重的工作”。 從而創(chuàng)造流暢的用戶體驗(yàn)并增強(qiáng)用戶滿意度。
應(yīng)用生命周期被分離出四個(gè)步驟:
1.Load
2.Idle
3.Animation
4.Response
Load
在用戶操作web應(yīng)用之前,web應(yīng)用就已經(jīng)被加載好了。 這是應(yīng)用生命周期的第一步并且最重要的是這一步旨在將負(fù)載時(shí)間減少(理想情況下為1s)到盡可能小的數(shù)量。
Idle
應(yīng)用加載完之后,經(jīng)常是空閑狀態(tài);等待用戶操作。應(yīng)用加載完后到用戶操作之前的這段空閑時(shí)間一般是50ms,開(kāi)發(fā)者可以用這段時(shí)間坐一些耗時(shí)的工作, 例如加載一些用戶很快要用到的資源(圖片,視頻)。
專(zhuān)業(yè)提示:顯著減少加載時(shí)間的一個(gè)方法是首先加載UI的基礎(chǔ)節(jié)點(diǎn),然后在空閑階段引入其他元素。
Animate
當(dāng)用戶開(kāi)始操作應(yīng)用并且空閑期已經(jīng)結(jié)束時(shí),應(yīng)用程序必須對(duì)用戶交互(和輸入)做出適當(dāng)?shù)姆磻?yīng)而沒(méi)有任何明顯的延遲。
提示:研究表明,需要大約十分之一秒(在與UI元素交互之后)才注意到卡頓。 因此,在此時(shí)間范圍內(nèi)響應(yīng)用戶輸入是完美的。
當(dāng)對(duì)用戶交互的響應(yīng)涉及某種動(dòng)畫(huà)時(shí),可能會(huì)有一些挑戰(zhàn)。為了將動(dòng)畫(huà)穩(wěn)定到60幀每秒,每幀的限制為16ms - 這基本上是一秒除以60。
那么,瀏覽器的開(kāi)銷(xiāo)就應(yīng)該時(shí)10ms到12ms。 實(shí)現(xiàn)此目的的一種方法是預(yù)先執(zhí)行所有動(dòng)畫(huà)計(jì)算(在UI元素與之交互后的100ms內(nèi))。
瀏覽器渲染路徑和優(yōu)化空間
瀏覽器渲染路徑有以下幾個(gè)步驟:
1.js 2.樣式計(jì)算 3.布局 4.繪制 5.合成層(Layer composition)
在網(wǎng)頁(yè)上,頁(yè)面發(fā)生改變(要么css要么js導(dǎo)致的),瀏覽器重新計(jì)算受影響元素的樣式。如果元素的位置、形狀等影響布局的地方發(fā)生更改, 瀏覽器將檢查其他元素,創(chuàng)建新布局,重新繪制受影響的元素并將這些元素重新組合在一起。
但是,更改頁(yè)面元素的某些屬性可能會(huì)更改網(wǎng)頁(yè)的渲染路徑。例如,如果是繪畫(huà)相關(guān)的屬性,比如背景圖片或者文字顏色這些有變動(dòng), 布局不會(huì)受影響因?yàn)檫@沒(méi)有改變屬性的位置和形狀。其他屬性更改可能會(huì)使布局生成和繪制脫離渲染管道。
我們將會(huì)展示一些關(guān)于瀏覽器渲染的一些優(yōu)化。
js
js讓開(kāi)發(fā)者為用戶提供優(yōu)秀的動(dòng)畫(huà)效果和視覺(jué)體驗(yàn),因此js成為web應(yīng)用中必不可少的一部分。我們?cè)谏衔膶?duì)應(yīng)用生命周期的討論中提到, 瀏覽器有10ms~12ms去渲染每一個(gè)頁(yè)面。為了減輕js在渲染管道的負(fù)擔(dān)(譯者:因?yàn)閖s執(zhí)行時(shí)間過(guò)長(zhǎng)導(dǎo)致一頁(yè)面的渲染超過(guò)12ms), 在每個(gè)頁(yè)面(幀)中盡可能早地執(zhí)行所有JavaScript代碼非常重要,因?yàn)樗赡軙?huì)觸發(fā)渲染管道的其他區(qū)域。
使用 window.requesAnimationFrame() 方法很有必要,詳情請(qǐng)看MDN文檔
window.requestAnimationFrame()方法告訴瀏覽器您希望執(zhí)行動(dòng)畫(huà)并請(qǐng)求瀏覽器調(diào)用指定的函數(shù)以在下一次重繪之前更新動(dòng)畫(huà)。 該方法將回調(diào)作為在重繪之前調(diào)用的參數(shù)
requestAnimationFrame方法使得瀏覽器能在正確的時(shí)機(jī)引入瀏覽器并且防止掉幀。下面是一個(gè)例子:
function doAnimation() {// Some code wizardryrequestAnimationFrame(doAnimation); //schedule the next frame}requestAnimationFrame(doAnimation); 復(fù)制代碼在google開(kāi)發(fā)者工具里的performance一欄中,允許開(kāi)發(fā)者去記錄每一頁(yè)面(幀)繪制出來(lái)的情況,可以看到j(luò)s在web應(yīng)用中執(zhí)行的情況
雖然requestAnimationFrame方法是非常重要的工具,但是還是會(huì)有些js代碼非常耗費(fèi)資源。 網(wǎng)站在我們的操作系統(tǒng)的主線程上運(yùn)行,因此這些腳本可能會(huì)停止渲染管道的其他階段的執(zhí)行。要解決這個(gè)問(wèn)題,我們可以使用Web worker。
web worker 能讓我們?cè)谛碌木€程執(zhí)行那些耗費(fèi)資源的js代碼。詳情請(qǐng)看MDN文檔
“有了web worker就簡(jiǎn)單了,我們可以把耗時(shí)的工作丟給后臺(tái)進(jìn)程。worker進(jìn)程可以在不干擾用戶界面的情況下執(zhí)行任務(wù)。 一旦被創(chuàng)建,worker可以通過(guò)將消息發(fā)布到由該代碼指定的事件處理程序來(lái)向創(chuàng)建它的JavaScript代碼發(fā)送消息(反之亦然)。”
要使用此功能,你需要?jiǎng)?chuàng)建一個(gè)單獨(dú)的JavaScript文件,你的主程序?qū)⑸梢粋€(gè)Web worker。
樣式計(jì)算
樣式更改是任何Web應(yīng)用程序渲染管道的關(guān)鍵部分,因?yàn)槠湓厮璧臉邮礁臄?shù)量與樣式重新計(jì)算的性能成本成正比。 可以查看Paul’s website來(lái)了解css樣式影響渲染管道的細(xì)節(jié)。
除了樣式變更的數(shù)量,選擇器匹配應(yīng)該考慮到我們的渲染優(yōu)化列表中。選擇器匹配是指確定應(yīng)將哪些樣式應(yīng)用于任何給定DOM元素的過(guò)程。
某些樣式可能比其他樣式需要更多時(shí)間來(lái)處理,并且時(shí)間隨著受一個(gè)或多個(gè)樣式更改影響的元素?cái)?shù)量的增加而增加。 解決此問(wèn)題的合適方法是塊元素修改器(BEM)方法。它提供了很好的性能優(yōu)勢(shì),因?yàn)轭?lèi)匹配符合BEM方法,是現(xiàn)代瀏覽器中最快的選擇器。
布局
一個(gè)主要的性能瓶頸是布局抖動(dòng)。在js重新計(jì)算元素節(jié)點(diǎn)的位置和形狀時(shí),會(huì)導(dǎo)致瀏覽器重新布局。這樣,當(dāng)快速連續(xù)幾次完成時(shí),會(huì)導(dǎo)致強(qiáng)制同步布局。 在這篇文章中, Google的Paul Lewis,強(qiáng)調(diào)了可以采取的各種優(yōu)化措施,以防止強(qiáng)制同步布局。
繪制
瀏覽器在開(kāi)始填充屏幕像素時(shí)發(fā)生繪畫(huà)。這包括在屏幕上繪制所有視覺(jué)元素(譯者注:即在渲染樹(shù)上的節(jié)點(diǎn)都會(huì)被繪制,不管它在不在屏幕視窗內(nèi)), 這會(huì)生成多個(gè)平面,叫圖層。當(dāng)需要大面積繪制時(shí),尤其是頁(yè)面滾動(dòng)時(shí),繪制同樣會(huì)導(dǎo)致性能問(wèn)題。
繪制分析器,如上圖。這讓我們很容易就知道頁(yè)面的哪些區(qū)域被繪制和什么時(shí)候繪制的。
第一個(gè)多選框,Paint flashing,勾上之后頁(yè)面會(huì)以綠色高亮被重新繪制的部分。 這個(gè)頻率可以告訴我們繪畫(huà)是否會(huì)影響渲染管道上的性能問(wèn)題。
我們看看上面這張圖,可以看到在頁(yè)面滾動(dòng)時(shí)只有滾動(dòng)條被重新繪制了。表明Scotch.io網(wǎng)站的主頁(yè)上有做瀏覽器繪制的優(yōu)化。
合成層(Layer Composition)
這是瀏覽器渲染管道的最后一步,它包含了重要的瀏覽器結(jié)構(gòu)-圖層(Layers)。瀏覽器引擎通過(guò)首先考慮樣式和元素以及它們的排序方式來(lái)進(jìn)行一些層管理。 然后嘗試找出頁(yè)面所需的圖層并相應(yīng)地更新圖層樹(shù)。
接下來(lái),瀏覽器合成這些圖層并在屏幕上顯示它們。 當(dāng)瀏覽器必須繪制彼此重疊的頁(yè)面元素并且彼此存在于同一層中時(shí),會(huì)出現(xiàn)由于繪制而導(dǎo)致的性能瓶頸。
要解決此問(wèn)題,所涉及的元素必須存在于單獨(dú)的層中。 這可以通過(guò)改變CSS屬性并將其屬性設(shè)置為transform來(lái)實(shí)現(xiàn):
<element_to_promote> {will-change: transform; } 復(fù)制代碼然而,應(yīng)該注意的是,層的增加意味著花在層管理和合成上的時(shí)間增加。 使用Chrome開(kāi)發(fā)工具,可以查看頁(yè)面上的所有圖層,如下所示:
打開(kāi)chrome開(kāi)發(fā)者工具,點(diǎn)擊hamburger菜單按鈕(三個(gè)豎著的點(diǎn)),選擇more Tools,選擇Layers。
轉(zhuǎn)載于:https://juejin.im/post/5c5410b3e51d450135283ad2
總結(jié)
- 上一篇: 【Linux】rpm常用命令及rpm参数
- 下一篇: 常用扩展方法