如何提高web頁面的性能


1.瀏覽器渲染原理解析

想要提高網頁的性能,首要的便是要理解瀏覽器渲染原理,下面關於瀏覽器的原理解析,我們以chrome內核webkit為例,其他內核的瀏覽器原理也基本大同小異,可觸類旁通。

 

如上圖所示,瀏覽器解析頁面步驟可分為:

* 解析HTML(HTML Parser)

* 構建DOM樹(DOM Tree)

* 構建CSSOM樹(Style)

* 構建渲染樹(Render Tree)

* 頁面布局(Layout)

* 繪制渲染樹(Painting)

這一過程可在chrome開發者工具的時間線中觀察:

 

這里我們簡要說一下以下四個概念:

* 布局(layout)

布局也稱為重排或回流,布局流程輸出的是一個“盒模型”,它會精確地捕獲每個元素在視口內的精確位置和尺寸,HTML就是采用基於流的布局模型,頁面元素的變動往往可能導致回流的發生,而回流的頻發發生亦是影響頁面性能的重要因素,另外,處於流后置位通常不會影響前置位的幾何特征,故對后置位的修改往往比對前置位的修改對頁面整體的影響要低。

* 繪制(paint)

繪制即是對DOM所分割的層(layer)進行對應的繪制,頁面的回流一般都會伴隨着重繪,但重繪行為的出現不一定伴隨回流。

* 渲染層

層(layer)的概念對於有設計基礎的人來說應該不陌生,我們平面直觀所見到的圖像是基於空間圖層的重疊得到的,一般來說,擁有相同坐標空間的節點屬於同一個渲染層。渲染層最初是用來實現層疊上下文,以此來保證頁面元素以正確的順序合成(composite),實現半透明重疊等效果。

創建渲染層的條件:

  * 根元素(HTML)

  * 有明確的position屬性(relative,fixed,sticky,absolute)

  * 透明的(opacity小於1)

  * 有css濾鏡(filter)

  * 有css mask 屬性

  * 當前有對於 opacity,transform,fliter,backdrop-filter 應用動畫

  * overflow屬性不為visible

  * 等等......

* 合成層

合成層是特殊的渲染層,每個合成層有單獨的繪圖層,繪圖層中的繪圖上下文負責輸出該層的位圖,位圖儲存在共享內存中,作為紋理上傳到GPU,最后由GPU將多個位圖進行合成,最后繪制到屏幕上,而相對於合成層,一般的渲染層是和其第一個擁有繪圖層的父層共用一個的繪圖層的,提升為合成層后當需要repaint或reflow本身,不影響其它層,另外,合成層的位圖會直接交由GPU合成處理,效率比CPU高。

渲染層提升為合成層的觸發原因:

  * 直接原因

    * iframe video canvas flash 元素 有 3D transform

    * backface-visibility 為 hidden

    * 對 opacity、transform、fliter、backdropfilter 應用了 animation 或 transition

    * will-change(設置為 opacity、transform、top、left、bottom、right(其中 top、left 等需要設置明確的定位屬性,如 relative 等))

   * 后代原因

    * 有合成層后代同時本身有 transform、opactiy(小於 1)、mask、fliter、reflection 屬性

    * 有合成層后代同時本身 overflow 不為 visible

    * 有合成層后代同時本身 fixed 定位

    * 有 3D transform 的合成層后代同時本身有 preserves-3d 屬性

    * 有 3D transform 的合成層后代同時本身有 perspective 屬性

  * 重疊原因

    * 元素的 border box(content + padding + border) 和合成層的有重疊,margin 的重疊無效

    * 動畫運行期間,元素可能和其他元素有重疊

 

2.影響頁面性能的操作及優化分析

* 頻繁操作DOM元素

使用js腳本頻繁地操作DOM元素是影響頁面性能的一大因素,頻繁地對DOM進行操作可能導致頁面重繪和回流的頻繁發生,從而導致頁面卡頓和性能消耗問題,從細節上可按如下方法進行優化:

1)使用文檔片段

var fragment = document.createDocumentFragment();

//一些基於fragment的大量DOM操作
......

document.getElementById('myElement').appendChild(fragment);

2)設置DOM元素的display樣式為none再操作該元素

var myElement = document.getElementById('myElement');
myElement.style.display = 'none';

//一些基於myElement的大量DOM操作
......

myElement.style.display = 'block';

3)復制DOM元素到內存中再對其進行操作

var old = document.getElementById('myElement');
var clone = old.cloneNode(true);

//一些基於clone的大量操作
......

old.parentNode.replaceChild(clone, old);

4)用局部變量緩存樣式信息從而避免頻繁獲取DOM數據

//bad operation

for (var i = 0; i < paragraphs.length; i++){
    paragraphs[i].style.width = box.offsetWidth + 'px';
}

//better operation

var width = box.offsetWidth;
for (var i = 0; i < paragraphs.length; i++){
    paragraphs[i].style.width = width + 'px';
}

5)合並多次DOM操作

//bad operation

var left = 10, top = 10;
el.style.top = top;
el.style.left = left;

//better operation

el.style.cssText += "; left: " + left + "px; top: " + top + "px;";

//better operation(將樣式內容設置於某一類名,再進行元素類名綁定)

el.className += " theclassName";

*css動畫造成頁面不流暢問題分析優化

使用css3動畫造成頁面的不流暢和卡頓問題,其潛在原因往往還是頁面的回流和重繪,減少頁面動畫元素對其他元素的影響是提高性能的根本方向,而實現可如下:

1)設置動畫元素position樣式為absolute或fixed,可避免動畫的進行對頁面其它元素造成影響,導致其重排和重繪的發生;

2)避免使用margin,top,left,width,height等屬性執行動畫,用transform進行替代;

//bad operation

div {   
    height: 100px;   
    transition: height 1s linear;   
}   

div:hover {   
    height: 200px;   
}

//better operation

div {   
    transform: scale(0.5);   
    transition: transform 1s linear;   
}   

div:hover {   
    transform: scale(1.0);   
} 

總而言之,盡量用transform和opacity完成動畫的展示,因為這兩個屬性可以避免重排和重繪的發生。

頁面渲染的流水線其實可簡單表示為以下步驟,從性能方面考慮,應該盡量避開layout和paint兩個步驟,只觸發composite步驟,但目前能做到這一效果的只有transform和opacity兩個屬性,另外需要注意的是:只有元素提升為合成層的時候transform和opacity才不會觸發paint,否則依舊觸發。

3)合理的提升合成層,以減少頁面不必要的繪制和重排

合成層的好處是不會影響到其他元素的繪制和不被其他層所影響,因此,為了彼此之前的影響造成的性能損失,我們需合理的將動畫效果中的元素或固定元素提升為合成層。

提升合成層的最好方式是使用 CSS 的 will-change 屬性。將will-change 設置為 opacity、transform、top、left、bottom、right 可以將元素提升為合成層。

#target {
  will-change: transform;
}

will-target的兼容性如下:

對於還不兼容該屬性的瀏覽器,我們使用3D transform予以代替

#target {
  transform: translateZ(0);
}

對於像頁面頂部欄,側欄等固定不變的位置元素,我們也可將其提升為合成層以避免其被其他元素影響而發生重繪,但要注意,合成層的提升也意味着性能的消耗增加,我們必須通過調試以測出合理的臨界值,不能盲目提升合成層,此外,盲目提升合成層也可能造成重疊產生的額外合成層,容易導致層爆炸的出現,即頁面連鎖出現大量合成層默認提升,建議用google的timeline進行監控調試,避免出現不必要的意外消耗。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM