一、優化原因
谷歌的數據表明,一個有10條數據0.4秒可以加載完的頁面,在變成30條數據加載時間為0.9秒后,流量和廣告收入減少了20%。當谷歌地圖的首頁文件大小從100kb減少到70~80kb時,流量在第一周漲了10%,接下來的三周漲了25%。
騰訊的前端工程師根據長期的數據監控也發現頁面的一秒鍾延遲會造成9.4%的PV的下降,8.3%跳出率的增加以及3.5%轉化率的下降。
可以看出,性能優化商業上來說很重要。
但是,更重要的還是屏幕前我們的用戶,讓用戶在使用產品時有更快更舒適的瀏覽體驗,這算是一種前端工程師的自我修養。
二、優化目標
以讓用戶滿意為目標呈現網頁是終極的目標。當然頁面速度要快,不過快只是構成滿意的一個因素。
我們使用Google團隊提出的RAIL模型作為優化的目標。
目前國內包括騰訊在內的團隊都在使用這個評估方案。
Response:100ms內響應;
Animation:10ms內生成一幀;
Idle:最大程度增加空閑時間;
Load:1000ms內呈現內容;
1)以用戶為中心 User-centered
用戶是性能優化的中心,一切性能優化皆是為了用戶獲得更佳的體驗。
那么,我們的用戶如何評價性能延遲:
0-16ms | 用戶可以感知每秒渲染 60 幀的平滑動畫轉場。也就是每幀 16 毫秒 留給應用大約 10 毫秒的時間來生成一幀。 |
0-100ms | 在此時間窗口內響應用戶操作,他們會覺得可以立即獲得結果。時間再長,操作與反應之間的連接就會中斷。 |
100-300ms | 輕微可覺察的延遲 |
300-1000ms | 延遲感覺像是任務自然和持續發展的一部分(用戶覺得這是正常流,但不會覺得快) |
1000+ms (>1s) | 用戶的注意力將離開他們正在執行的任務。 |
10,000+ms (>10s) | 用戶感到失望,可能會放棄任務;之后他們或許不會再回來。 |
2)Response:100ms內響應
用戶大多數輸入,不管他們是在點擊按鈕、切換表單控件還是啟動動畫,如果未得到響應,操作與反應之間的連接就會中斷。用戶會注意到這個延遲,這將會造成不完美的體驗。
在用戶注意到滯后之前,我們有 100 ms的時間可以響應用戶輸入。
對於需要超過 500 毫秒才能完成的操作,始終提供反饋,則用戶不會陷入困惑。這也是一些交互設計師一直堅持處處操作有反饋的原因。
3)Animation: 10ms內生成一幀
在數學上來說,人眼感受到的幀數為60幀/s,則會認為是流暢的動畫。
1s/60 = 1000ms/60 = 16ms/幀;
也就是說加上每一幀的預算是16ms,減去瀏覽器繪制幀的時間,留給我們的大約只有10ms/幀。 如果超過這個時間,用戶眼中動畫的流暢度就會降低。
4)Idle:最大程度增加空閑時間
利用空閑的時間完成推遲的工作。
例如,盡可能減少預加載數據,以便您的應用快速加載,並利用空閑時間加載剩余數據。推遲的工作應分成每個耗時約 50 毫秒的多個塊。如果用戶開始交互,優先級最高的事項是響應用戶。
5)Load: 1000ms內呈現內容
在 1 秒鍾內網站加載完畢。
否則用戶的注意力會分散,他們處理任務的感覺會中斷。啟用漸進式渲染和在后台執行一些工作。將非必需的加載推遲到空閑時間段。
總結
上述的RAIL指標,我們可以用chrome自帶的timeline工具檢測:
RAIL步驟 | 關鍵指標 | 用戶操作 |
Response | 輸入延遲時間(從按下到繪制)小於 100 毫秒。 | 用戶點按按鈕(例如打開導航)。 |
Animation | 每個幀的工作(從 JS 到繪制)完成時間小於 16 毫秒。 | 用戶滾動頁面,拖動手指或看到動畫。 拖動時,應用的響應與手指位置有關。 |
Idle | 主線程 JS 工作分成不大於 50 毫秒的塊。 | 用戶沒有與頁面交互,但主線程應足夠用於處理下一個用戶輸入。 |
Load | 頁面可以在 1000 毫秒內就緒。 | 用戶加載頁面並看到關鍵路徑內容。 |
三、優化方法
1.加載性能優化
1)優化內容效率
評估每個資產的表現:其價值及其技術性能。
網頁往往會包含一些不必要的資源;或者更糟的是,包含的某些資源會影響網頁性能,同時還無法給訪問者或所處的網站帶來太大價值。

網頁上一直包含資源 A(例如某個輪播圖):
它提供給用戶的價值能否抵消下載並顯示它的開銷呢?
是否能夠評估並證明其價值?
該資源(特別是第三方資源)能否保持穩定的性能?
該資源是否處於或是否需要處於關鍵路徑中?
如果該資源不可用,是否會影響網頁的性能和用戶體驗?
不斷地去對資源做檢查,以給用戶展示想看到的高性能、有價值信息。
2)圖片優化
圖片是網頁中的必不可少的元素,烘托氛圍,傳達信息,作用很廣泛。同時,圖片也占據大量字節,有極大優化的潛力。
2.1 減少圖片資源
是否真的需要用圖片去表達,如果不需要可以盡量去減少使用。這是最直接的優化方式(嗯 我已經看見設計師提着刀過來了)。站在用戶和對方的角度思考一下,相信是可以達到共識的。
2.2選擇合適的圖片格式

矢量圖形使用線、點和多邊形來表示圖像,放大后仍然可以保持清晰,最適用於高分辨率屏幕和需要以不同尺寸顯示的場景。
光柵圖形通過對矩形格柵內的每個像素的值進行編碼來表示圖像,適合場景豐富的情況。
如果可以用css3等替代圖片,那是極好的。
如果不能被替代,可以根據場景復雜度進行選擇;場景簡單,用矢量圖;場景復雜,用光柵圖。

光柵圖有很多格式,常見而熟知的有gif,png,jpg,webp等,每種格式都有自己獨特的編碼方式。
2.3深度優化
現今電子設備分辨率層出不窮,對圖片也有不一樣的要求。
CSS中1px實則是一個虛擬的相對像素,往往 1css像素 = ?設備像素。這里的問號根據分辨率不同有着多種情況。如下圖:

100 x 100 像素的圖像由 10,000 個像素組成。瀏覽器為每個通道分配 256 個值(色階),也就是每個通道 8 位 (2 ^ 8 = 256),每個像素 4 個字節(4 個通道 x 8 位 = 32 位 = 4 個字節)。
10,000 個像素 x 4 個字節 = 40,000 個字節

40,000 個字節/1024 = 39 KB

由圖可知,圖片尺寸的增加,圖片所占用的內存越大。物理屏幕的分辨率加倍,所需的像素數不只是加倍,而是增加到原來的四倍。
因此,選擇合適尺寸的圖片對於性能很重要。為合適的對象選擇合適尺寸從的圖片是有利於性能優化的。
上面提到,光柵圖有多種圖片格式,但是需要根據瀏覽器和需求選擇合適的格式:

GIF支持各種格式,但是畫質不高。png支持透明度和所有瀏覽器但是體積較大。jpg不支持透明度和動畫,但是支持所有瀏覽器而且體積小。后起新秀XR和Webp對於瀏覽器的支持不足,但是Webp體積小,比jpg還要小30%,可以考慮向下兼容。
而png的可塑性也可圈可點,只要降低通道顏色的數量,體積可以小到驚喜。畫質的改變見仁見智。

國外前端從業者總結出這樣的使用方法:
需要動畫:gif
不需要動畫
需要高畫質細節清晰
需要>256色圖片:png24
不需要>256色:png8
不需要高畫質細節清晰:jpg

圖片優化指南
優化gif圖:http://www.lcdf.org/gifsicle/
優化jpg圖:http://jpegclub.org/jpegtran/
png無損優化:http://optipng.sourceforge.net/
png有損優化:https://pngquant.org/
svg壓縮:https://github.com/svg/svgo
2.4提供縮放的圖像
提供多余像素的開銷只會讓瀏覽器代替我們重新縮放圖像,減少並優化渲染網頁所需總字節數的大好機會因此被錯過。還要注意的是,尺寸調整不僅受圖像縮減像素數的影響,還受其自然尺寸的影響。


應確保多余像素數最少,並確保特別是較大資產以盡可能接近其顯示尺寸的尺寸提供。
2.5合並圖像
優化原理來於web優化中的減少http請求數量,通過減少頁面圖片的數量來實現。
合並圖片后,可以通過css的background-image、background-size、background-position屬性定位使用單個圖片。

合並圖片后的圖片總大小會變得更小,但是需要注意:
a.合並主要用於圖標和按鈕等小而多的元素,復雜的圖像盡量不合並,尤其是jpg格式。
b.logo和內容圖片不要合並,不能破壞html本身的語義結構。
c.盡可能讓顏色值相近的圖片合並到同一張雪碧圖里面。
d.空白也要占用空間,控制圖片之前的空隙。
e.追求優化度可以手動合並,追求速度可以使用工具合並,例如cssGaga等。


3)字體優化
如果對字體進行優化,再通過制定明智的策略對字體在網頁上的加載和應用方式作出規定,就可以幫助減小網頁總大小,並縮短網頁渲染時間.

3.1選取字體
重要的是考慮它支持的字符集。
目前字體格式有四種:WOFF2、WOFF、EOT 和 TTF。
WOFF2 | 許多瀏覽器都未支持 |
WOFF | 支持廣泛,較舊的瀏覽器不支持 |
EOT | 只有IE支持 |
TTF | 部分IE支持 |
所以字體的選用可以參照:
-
-
將 WOFF 2.0 變體提供給支持它的瀏覽器。
-
將 WOFF 變體提供給大多數瀏覽器。
-
將 TTF 變體提供給舊 Android(4.4 版以下)瀏覽器。
-
將 EOT 變體提供給舊 IE(IE9 版以下)瀏覽器。
-
3.2壓縮字體大小
字體是字形的集合;
每個字形都是一組描述字母形狀的路徑。
各個字形不同,但它們仍然包含大量相似信息,這些信息可通過 GZIP 或兼容的壓縮工具進行壓縮。
考慮使用 Zopfli 壓縮處理 EOT、TTF 和 WOFF 格式。Zopfli 是一種兼容 zlib 的壓縮工具,提供的文件大小壓縮率比 gzip 高大約 5%。
3.3 瀏覽器尋找字體順序

如果希望較新的瀏覽器使用 WOFF2,則應將 WOFF2 聲明置於 WOFF 之上,依此類推。
3.4 字體渲染
字體請在css里面,所以將遠遠滯后於其他關鍵資源請求的處理,並且在獲取資源之前,可能會阻止瀏覽器渲染文本。

-
-
Safari 會在字體下載完成之前延遲文本渲染。
-
Chrome 和 Firefox 會將字體渲染暫停最多 3 秒,之后他們會使用一種后備字體。並且字體下載完成后,他們會使用下載的字體重新渲染一次文本。
-
IE 會在請求字體尚不可用時立即使用后備字體進行渲染,然后在字體下載完成后進行重新渲染。
-
4)關鍵渲染路徑優化
從收到 HTML、CSS 和 JavaScript 字節到對其進行必需的處理,從而將它們轉變成渲染的像素這一過程中有一些中間步驟,優化性能其實就是了解這些步驟中發生了什么 - 即關鍵渲染路徑。
4.1 對象模型
HTML 標記轉換成文檔對象模型 (DOM);CSS 標記轉換成 CSS 對象模型 (CSSOM)。DOM 和 CSSOM 是獨立的數據結構。



瀏覽器根據字節 → 字符 → 標記 → 節點 → 對象模型的順序,逐步創建了dom樹。
4.2 CSS對象模型
瀏覽器構建頁面的 DOM 時,在文檔的 head 部分遇到了一個 link 標記,該標記引用一個外部 CSS 樣式表:style.css。由於預見到需要利用該資源來渲染頁面,它會立即發出對該資源的請求,並返回樣式的內容:
與處理 HTML 時一樣,我們需要將收到的 CSS 規則轉換成某種瀏覽器能夠理解和處理的東西。因此,我們會重復 HTML 過程,不過是為 CSS 而不是 HTML。

頁面上的任何對象計算最后一組樣式時,瀏覽器都會先從適用於該節點的最通用規則開始(例如,如果該節點是 body 元素的子項,則應用所有 body 樣式),然后通過應用更具體的規則(即規則“向下級聯”)以遞歸方式優化計算的樣式。所以,cssdom樹有樹的形狀。
每個瀏覽器都提供一組默認樣式(也稱為“User Agent 樣式”),即我們不提供任何自定義樣式時所看到的樣式。我們的樣式只是替換這些默認樣式。

DevTools 中記錄時間線並尋找“Recalculate Style”事件:與 DOM 解析不同,該時間線不顯示單獨的“Parse CSS”條目,而是在這一個事件下一同捕獲解析和 CSSOM 樹構建,以及計算的樣式的遞歸計算。
4.3 渲染樹建設、布局和繪制
CSSOM 樹和 DOM 樹合並成渲染樹,然后用於計算每個可見元素的布局,並輸出給繪制流程,將像素渲染到屏幕上。
4.3.1 DOM和CSSOM合並為渲染樹

遍歷網頁上所有可見的(除了display:none) DOM 內容,以及每個節點的所有 CSSOM 樣式信息。
過程:
從 DOM 樹的根節點開始遍歷每個可見節點;
對於每個可見節點,為其找到適配的 CSSOM 規則並應用它們;
發射可見節點,連同其內容和計算的樣式。
注: visibility: hidden 與 display: none 是不一樣的。前者隱藏元素,但元素仍占據着布局空間(即將其渲染成一個空框),而后者 (display: none) 將元素從渲染樹中完全移除,元素既不可見,也不是布局的組成部分。
4.3.2 布局(自動重排)

布局流程的輸出是一個“盒模型”,它會精確地捕獲每個元素在視口內的確切位置和尺寸:所有相對測量值都轉換為屏幕上的絕對像素。
4.3.3 繪制(柵格化)

-
- “Layout”事件在時間線中捕獲渲染樹構建以及位置和尺寸計算。
- 布局完成后,瀏覽器會立即發出“Paint Setup”和“Paint”事件,將渲染樹轉換成屏幕上的像素
執行渲染樹構建、布局和繪制所需的時間將取決於文檔大小、應用的樣式,以及運行文檔的設備:文檔越大,瀏覽器需要完成的工作就越多;樣式越復雜,繪制需要的時間就越長。
4.3.4 渲染
關鍵渲染路徑要求我們同時具有 DOM 和 CSSOM 才能構建渲染樹。這會給性能造成嚴重影響:HTML 和 CSS 都是阻塞渲染的資源。 HTML 顯然是必需的,因為如果沒有 DOM,我們就沒有可渲染的內容,但 CSS 的必要性可能就不太明顯。


第一個聲明阻塞渲染,適用於所有情況。
第二個聲明同樣阻塞渲染:“all”是默認類型,如果您不指定任何類型,則隱式設置為“all”。因此,第一個聲明和第二個聲明實際上是等效的。
第三個聲明具有動態媒體查詢,將在網頁加載時計算。根據網頁加載時設備的方向,portrait.css 可能阻塞渲染,也可能不阻塞渲染。
最后一個聲明只在打印網頁時應用,因此網頁首次在瀏覽器中加載時,它不會阻塞渲染。
“阻塞渲染”僅是指瀏覽器是否需要暫停網頁的首次渲染,直至該資源准備就緒。無論哪一種情況,瀏覽器仍會下載 CSS資源,只不過不阻塞渲染的資源優先級較低罷了。
4.3.5 使用js增加交互性
我們的腳本在文檔的何處插入,就在何處執行。
當 HTML 解析器遇到一個 script 標記時,它會暫停構建 DOM,將控制權移交給 JavaScript 引擎;等 JavaScript 引擎運行完畢,瀏覽器會從中斷的地方恢復 DOM 構建,也就延緩了首次渲染。
“優化關鍵渲染路徑”在很大程度上是指了解和優化 HTML、CSS 和 JavaScript 之間的依賴關系譜。
無論我們使用 <script> 標記還是內聯 JavaScript 代碼段,瀏覽器都會先暫停並執行腳本,然后才會處理剩余文檔。不過,如果是外部 JavaScript 文件,瀏覽器必須停下來,等待從磁盤、緩存或遠程服務器獲取腳本,這就可能給關鍵渲染路徑增加數十至數千毫秒的延遲。
為此,我們可以將腳本標記為——異步:

4.3.6 評估關鍵渲染路徑
每個可靠性能策略的基礎,准確的評估和檢測必不可少。
Lighthouse 是一個網絡應用審核工具,可以對特定頁面運行一系列測試,然后在匯總報告中顯示頁面的結果。

2.渲染性能優化
2.1 優化js執行
測量 JavaScript 開銷和性能情況的最佳方法是使用 Chrome DevTools
如果發現有長時間運行的 JavaScript,則可以在 DevTools 用戶界面的頂部啟用 JavaScript 分析器:


2.2 避免大型復雜的布局
與樣式計算相似,布局開銷的直接考慮因素如下:
需要布局的元素數量。
這些布局的復雜性。
2.2.1盡可能避免布局操作
當您更改樣式時,瀏覽器會檢查任何更改是否需要計算布局,以及是否需要更新渲染樹。對“幾何屬性”(如寬度、高度、左側或頂部)的更改都需要布局計算。

2.2.2 使用 flexbox 而不是較早的布局模型

2.3 Composite:渲染層合並
利用合成層對於提升頁面性能方面有很大的作用。
合成層的位圖,會交由 GPU 合成,比 CPU 處理要快;當需要 repaint 時,只需要 repaint 本身,不會影響到其他的層;對於 transform 和 opacity 效果,不會觸發 layout 和 paint。
2.3.1合成層好處
a.提升動畫效果
合成層的好處是不會影響到其他元素的繪制,可以減少動畫元素對其他元素的影響,從而減少 paint。
使用 CSS 的 will-change 屬性:
will-change 設置為 opacity、transform、top、left、bottom、right 可以將元素提升為合成層。



b.減少繪制區域
對於不需要重新繪制的區域應盡量避免繪制,以減少繪制區域。
比如一個 fix 在頁面頂部的固定不變的導航 header,在頁面內容某個區域 repaint 時,整個屏幕包括 fix 的 header 也會被重繪。
c.合理管理合成層
創建一個新的合成層並不是免費的,它得消耗額外的內存和管理資源。實際上,在內存資源有限的設備上,合成層帶來的性能改善,可能遠遠趕不上過多合成層開銷給頁面性能帶來的負面影響。
下面的例子中,創建了2000個相同的div元素,第一個未提升為合成層,第二個提升為合成層:


2.3.2合成層原理


DOM 樹中得每個 Node 節點都有一個對應的 LayoutObject ;
一般來說,擁有相同的坐標空間的 LayoutObjects,屬於同一個PaintLayer,特殊情況會為一些特殊的 LayoutObjects 創建一個新的渲染層;
合成層擁有單獨的 GraphicsLayer,而其他不是合成層的渲染層,則和其第一個擁有 GraphicsLayer 父層公用一個。

-
- 根元素(HTML)
- 有明確的定位屬性(relative、fixed、sticky、absolute)
- 透明的(opacity 小於 1)
- 有 CSS 濾鏡(fliter)
- 有 CSS mask 屬性
- 有 CSS mix-blend-mode 屬性(不為 normal)
- 有 CSS transform 屬性(不為 none)
- backface-visibility 屬性為 hidden
- 有 CSS reflection 屬性
- 有 CSS column-count 屬性(不為 auto)或者 有 CSS column-width 屬性(不為 auto)
- 當前有對於 opacity、transform、fliter、backdrop-filter 應用動畫
滿足基本條件后,達到下面的條件是才可能被提升為合成層:
硬件加速的 iframe 元素(比如 iframe 嵌入的頁面中有合成層);
video 元素;
對 opacity、transform、fliter、backdropfilter 應用了 animation 或者 transition;
will-change 設置為 opacity、transform、top、left、bottom、right。
在實際使用時可以參考這些做法。
1.lighthouse




這里看到perfomance是86分(我們只關心在這里的性能結果)
點擊查看詳細的性能報告:


可以看到原因是由於兩張較大的圖片引起的。
並且給出建議:使用webp圖片,可以更快的打開頁面。
十分詳盡,值得參考。
2.pagespeed
pagespeed也是chrome瀏覽器的一個擴展插件。
這個驗證需要已經可以公共互聯網訪問的頁面,所以選用了一個已經上可以公共訪問的地址。



進入分析階段,pagespeed比lighthouse的用時稍微久一些。
得到大致結果后,可以點擊“ need more " 查看詳細:

可以得到十分詳盡的優化建議和已經做好的優化。點開其中的一個優化措施:
這是對於圖片優化的一個建議,精確到每一張圖片。十分有效。
以上就是推薦的兩款插件,使用方便,結果清晰准確。
總結
性能優化的意義在於給用戶提供舒適快速的網頁瀏覽體驗。
通過RAIL目標,結合加載和渲染性能優化,用檢測工具加以監測和修正,以達到最佳的性能。