前端入門之網頁性能優化(三)

好,其實網頁性能優化講到上一篇,對於入門的同學來講就已經差不多了。現在做一下小結。

對於HTML、CSS和JavaScript都通用的一個優化策略就是Minify,Compress,和Cache(縮小,壓縮和緩存)。對於CSS來講就是添加媒體查詢和內嵌CSS。對於JavaScript來講就是延緩載入(用onload事件)和在<script>欄位里添加async關鍵字。總結起來就是需要優化CRP(關鍵呈現路徑)的三個指標:1. 關鍵資源數 2. 關鍵位元組大小 3.關鍵路徑長度。

看這個例子,我們可以看到關鍵資源數量是5個,分別是1個HTML,1個CSS和3個JS文件,關鍵位元組量就是這個五個文件大小的加和,而關鍵路徑長度就是5,即5次請求才能載入完。

到這裡我們就弄明白如何對一個網頁做一個基礎的性能優化了。實際上,一般設備的刷新頻率是60次每秒,也就是60hz,那我們的網頁也要做到60FPS才算是比較合格的,也就是每1秒需要展示60幀的內容,那麼對於每一幀的渲染其實只有16ms。如果瀏覽器花費較長的時間才能渲染一幀,幀速度下降就出出現卡頓,這會造成很差的用戶體驗。其實可以把一幀的渲染流程看做一個pipline

一般情況下,都是會通過JavaScript來修改樣式的,不過也有其他方式比如CSS Animations,tansitions或者是新的web animations API等等。也就是做出修改-渲染樣式-計算布局-像素著色-圖層合成。但並不是每一次修改都需要經歷完這幾個步驟。第一種情況,如果通過JS或者CSS修改了網頁的樣式和元素的幾何結構,那麼瀏覽器就需要重新計算樣式,計算布局,染色,合成多個圖層。第二種情況,如果通過JS或者CSS只修改了網頁的樣式,並沒有修改幾個結構,比如換了一張照片,或者只是添加了一個陰影,這樣的話瀏覽器就不需要重新計算布局,只需要計算樣式,染色,合成圖層。點這裡可以詳細了解css的哪些操作會出發哪些步驟。

下面就要從更高的層面去分析可優化的地方了。一個網路應用的生命周期一般分為四個部分,即載入,閑置,動畫,響應。而一般情況下載入的時間在1s內完成就不會影響用戶體驗。載入完成後,一般用戶是會有50ms的閑置時間,那這50ms對於我們完成一些耗時的計算或者處理是非常有幫助的。利用這個時間做一些不太重要的工作來確保此後的所有操作都能夠及時響應。對於交互來說,當用戶點擊了一個按鈕,在100ms內做出響應的話就不會有卡頓的表現。對於動畫來說依然是要做到60fps,即每一幀的渲染時間要在16ms以內。比如用戶滾動屏幕或者其它的一些動畫。

現在我們看上面的pipline。對於開始的JavaScript,我們首先要弄清楚你寫代碼的步驟並不是瀏覽器執行的步驟,因為JavaScript的引擎很複雜,因此你就沒有必要在這上面去做微優化。微優化就是說你沒有必要糾結於選擇for循環還是while循環,因為你也不知道引擎是怎麼處理這兩個循環的,並且優化的效果小到可以忽略不計。因此我們要把優化思路放到其他方向。如果能在網頁載入的1000ms內,或者閑置的50ms內,響應的100ms,動畫的16毫秒內完成JavaScript的處理不就不影響用戶體驗了嗎?也就是說要在每一幀最開始的時候就去執行JavaScript。requestAnimationFrame( )就能夠實現讓JavaScript儘早在每一幀的開始去執行。要想達到60fps的動畫效果,每一幀的渲染時間最多只能1000ms/60 = 16ms。但是實際情況是瀏覽器還有其他的工作要做,留給渲染的時間只有10ms。JavaScript的執行也最多只能佔3-4ms,因為後面還有樣式,布局,染色和合成圖層的工作要去做。在舊時代,實現這個功能的是setTimeOut( )和setInterval( )這兩個函數。這裡再介紹一個工具Web Workers(詳細介紹戳這裡)。它能夠開闢出來一條獨立於操作系統之外的新線程,可以用來處理執行時間比較長的JavaScript。

但是要注意主線程與Web Works線程無法相互通信。

在pipline裡面JavaScript的下一步就是樣式(style)。要知道修改樣式所耗費的成本和需要修改樣式的元素的個數是成線性增長的。因此在這裡要根據實際情況注意控制需要修改樣式的元素的數量。那除了這一點,還有一點就是要注意選擇器匹配的問題。選擇器匹配是指確定某些樣式是否應該應用到任何給定DOM元素的過程。複雜的選擇器會給瀏覽器帶來更多的工作量。比如.box:nth.title就比.box-three要複雜,當有大量的複雜選擇器的時候,對於瀏覽器就會產生比較大的性能消耗。修改樣式的下一步是聚酸布局,在這一步里,要注意避免強制同步布局。因為在寫代碼的時候,一不小心就會犯這個錯誤。

看著三段代碼,上面兩段都觸發了強制同步布局。因為訪問.scrollY和訪問.offsetHeight都會導致瀏覽器運行布局(會觸發layout的事件看這裡)。因此每次都是先布局,然後修改樣式,根據pipline,然後又布局。這樣不好。而最下面的那段程序就不會導致強制同步布局,因為它在循環體外面只訪問了一次.offsetWidth,這就是個很好的策略,布局運行一次,然後批量更新樣式。那麼修改前兩段代碼的方式就是要在這一幀最開始的時候讀取一次布局屬性,然後在這一幀快結束的時候批量修改樣式,這是一步小小的修改,但是卻大大的優化了性能。修改如下圖

最後就是paint和composite,即染色和渲染層的合成。對於paint,要避免不要每次都對整個頁面進行染色,只對需要重新染色的元素進行染色就可以了。類似於PS的多個圖層,網頁在處理的時候也是有很多圖層,因為如果只有一個圖層,那麼在染色的時候整個頁面都需要重新染色,每一幀都這麼做的話是很耗費性能的。因此可以分成多個圖層,染色這個過程就可以在相應的圖層上去做,最後再合成展示。對於每一個元素,可以加上這個屬性{will-change:transform}就可以把當前元素變成一個新的圖層。但是如果創建了很多新的圖層也是對性能很不好的,所以需要權衡。對於10ms完成渲染一幀來講,更新圖層和合成圖層都不應該超過2ms。關鍵在於為自己的項目創建合適的圖層數量。

收尾之前再來說一點技術名詞的發音哈,對於咱們經常讀的JavaScript[d?ɑ?v? skr?pt],注意音標[d?ɑ?v?],不要讀成「假娃」,這樣很難聽!要注意前面一個音要舌尖頂著上顎,第二個音要上牙咬著下嘴唇,來,試著發一下音!還有Composite,[k?mpɑ?z?t],不要讀成compousit.美音有很多o都發[ɑ?]音,這個要注意一下。

好了,網頁性能優化的方法很多很多。因為本文講的是入門,所以大概就先介紹到這裡,希望對於新入門的同學會有所幫助。他日等我也掌握更多技能,更有經驗之後再來做補充。

前端入門之網頁性能優化(二)

前端入門之網頁性能優化(一)

推薦閱讀:

如何在不刷新頁面的情況下改變URL
十分鐘搞定JSON和JSON對象
The Story of Me Becoming A Front-End Developer in the Past Two Years

TAG:前端工程师 | 前端入门 | 前端性能优化 |