頁面渲染:性能分析

CSS屬性的渲染影響列表>>css


performance介紹


Chrome DevTools的performance面板能夠記錄和分析頁面在運行時的全部活動。html

配合無痕模式,能夠避免chrome插件的影響。jquery

幀率圖

1. 錄製查看

上面部分是幀數信息:

  • 1312.3ms:一幀的時間
  • 1fps: 每秒的幀數

下面部分是網頁快照,瀏覽器按照必定時間間隔截取。ajax

2. 實時查看

快捷鍵ctrl + shift + p能夠實時查看幀率chrome

還能夠進行其它配置

  • Paint Flashing 高亮顯示網頁中須要被重繪的部分。
  • Layer Borders 顯示Layer邊界。
  • FPS Meter 每一秒的幀細節,幀速率的分佈信息和GPU的內存使用狀況。
  • Scrolling Performance Issues 分析鼠標滾動時的性能問題,會顯示使屏幕滾動變慢的區域。
  • Emulate CSS Media 仿真CSS媒體類型,查看不一樣的設備上CSS樣式效果,可能的媒體類型選項有print、screen

火焰圖

  • Loading:網絡通訊和HTML解析
  • Scripting:Javascript執行
  • Rendering:樣式計算和佈局,即重排
  • Painting:重繪 對應的詳細事件 >>>

怎麼看出性能問題?

1. 紅色小三角

常見的緣由爲:

  • handler took xxx ms 操做消耗太多時間
  • forced reflow is likely performance bottleneck 「強制同步佈局」可能會致使性能問題,一般是由於修改樣式後讀取屬性,致使了瀏覽器必須從新渲染以獲取最新的屬性值。

2. 佈局抖動

「佈局抖動」是指反覆出現「強制同步佈局」狀況。 這種狀況會在 JavaScript 從 DOM反覆地寫入和讀取時出現,將會強制瀏覽器反覆從新計算佈局。佈局抖動也會致使長幀,使頁面卡頓。

3. 長幀

長幀表示一幀的時間過長,會影響頁面的加載速度與動畫的流暢性,這時會感覺到頁面加載慢或動畫卡頓。

動畫播放時每秒的幀數最好可以達到60幀,也就是每幀16.6ms。api


實例


1. 解析html(不包含js css外部文件)

  • readystatechange(第一個)(文檔已加載和解析) 此時狀態爲interactive,表示文檔已加載和解析但資源仍在加載,該狀態一般緊接着會觸發DomContentLoaded。
  • DOMContentLoaded (DOM樹構建完成) html文檔被加載和解析成功,DOM樹構建完成時會觸發。
  • Recalculate Style(CSSOM構建完成) 經過添加和刪除元素,更改屬性、類或經過動畫來更改 DOM,全都會致使瀏覽器從新計算元素樣式,在不少狀況下還會對頁面或頁面的一部分進行佈局(即自動重排)。從新計算樣式的步驟能夠分爲兩步:
  1. 瀏覽器計算出給指定元素應用哪些類、僞選擇器和 ID。
  2. 從匹配選擇器中獲取全部樣式規則,並計算出此元素的最終樣式。
  • readystatechange(第二個)(文檔已加載和解析,且資源也加載完成) 此時狀態爲complete,表示文檔和資源都已加載完成,該狀態一般緊接着會觸發load。
  • load事件 文檔和資源都已加載完成時會觸發。更多頁面加載完成事件>>
  • Layout 佈局幾乎老是做用在整個文檔,但仍是主要看影響的節點個數。

2. 解析html(包含js css外部文件)

  • Evaluate Script (執行js)
  • layout變爲不在ParseHtml中執行 多是由於CSS文件或JS文件的加載阻塞了整個頁面的渲染過程,由於js和css均可能對標籤進行樣式的設置。若是不存在文件,就不會存在等待加載的問題。

3. 改變背景色(重繪)

4. 改變高度(重排)

相對於重繪多了個Layout瀏覽器

5. 圖片資源加載(img或bg)

若是圖片標籤尺寸不變,則會觸發一次重繪性能優化


遵循的原則


1. 關於阻塞

  • css加載 不會阻塞 DOM樹的解析;CSS解析 會阻塞;
  • css加載和解析 會阻塞 js(因此內聯樣式不用加載性能較高,適用於第一屏)
  • js 會阻塞 DOM樹的解析 (由於js會改變DOM樹內容)
  • css引入的字體文件加載 也會阻塞 js , 頁面渲染

2. 關於與頁面渲染過程的對應

  1. js執行時:這時應該只是構建了前面部分的dom樹和CSSOM樹,由於js須要經過dom api和CSSOM api操做前面部分的標籤的內容和樣式。
  2. DOM樹構建完成:DomContentLoaded事件
  3. CSSOM構建完成、Render Tree構建完成:Recalculate Style
  4. Layout:Layout事件
  5. paint:Paint(圖片層繪製) 和 Composite Layers(圖片層合併),除了transform 或 opacity屬性以外,更改任何屬性始終都會觸發繪製Paint
  6. reflow重排:3 4 5步走一遍
  7. repaint重繪:3 5步走一遍
  8. 更改一個既不要佈局也不要繪製的屬性:3步 + Composite Layers,此行爲在678從新渲染步驟中開銷最小,適合動畫或滾動,具體好比transfrom opacity。

3. 關於chrom瀏覽器的一些行爲

  • 渲染隊列:瀏覽器存在一個渲染隊列,用於將屢次連續的重排和重繪操做變成一次。當你進行DOM的讀操做時,若是隊列不爲空,chrome會清空隊列,當即進行重排或重繪;若是爲空,chrome不會作出多餘的操做。
  • 佈局:佈局或重排中瀏覽器須要計算元素要佔據的空間大小及其在屏幕的位置,網頁的佈局模式意味着一個元素可能影響其餘元素,例如 <body> 元素的寬度通常會影響其子元素的寬度以及樹中各處的節點。
  • 繪製與合成:繪製通常是在多個表面(一般稱爲層)上完成的,所以瀏覽器須要將它們按正確順序繪製到屏幕上,以便正確渲染頁面。
  • css選擇器:對於複雜的css選擇器,瀏覽器須要花更多時間來肯定元素的樣式,所以以類爲中心的css編寫原則,老外比較推崇,好比:nth-last-child僞類能夠用獨立的類替代(否則它怎麼叫僞類( ̄▽ ̄)")。

性能優化


阻塞優化

  • js存在問題: 1)js加載和執行都會阻塞DOM解析和頁面渲染 2)若是引用第三方腳本,當第三方服務商請求延遲時,頁面會白屏; js解決辦法: 頁面能夠經過添加關鍵字defer和async來異步加載js。

  1. 二者都是異步加載,不一樣的是: async(參考ajax):異步加載時不會阻塞DOM解析和頁面渲染;執行時間爲加載完成時;執行時會阻塞,但這時可能DOM已經解析完成,甚至頁面已經渲染;另外會影響js文件執行順序。 defer:異步加載時不會阻塞DOM解析和頁面渲染;執行時間爲DOM解析完成以後、DOMContentLoaded事件以前(因此會阻塞DCLoad事件和jquery的ready事件);執行時DOM已經解析完成,只會阻塞頁面渲染。
  2. js文件加載順序 同步 > 異步 同是async 按加載完成順序 同是defer 按引入順序 async defer 正常不一塊兒用

減小從新渲染

關於CSSbash

  • 使用簡單的樣式表。樣式表越簡單,重排和重繪就越快。具體爲:
  1. 減低選擇器的複雜性,少用僞類;使用以類爲中心的方法,例如BEM;
  2. 減小必須計算其樣式的元素的數量,應當儘量減小聲明爲無效的元素的數量。
  • 減小DOM元素層級。重排和重繪的DOM元素層級越高,成本就越高。
  • 多利用display:none。display:none的元素沒有在渲染樹,於是也不會進行重排和重繪
  • 使用CSS動畫而不是JS動畫。CSS動畫優於JS動畫,是因爲CSS改變的是translate的值,不會引發offsetLeft、offsetTop等位置值的改變。
  • 使用absolute而不是float。position屬性爲absolute或fixed的元素在重排的開銷比float少,由於不用考慮它對其餘元素的影響。
  • 使用div而不是table。由於一個很小的改動可能都會引發整個table的從新佈局,好比說td內容改變。

關於JS網絡

  • DOM的多個讀操做(或多個寫操做)應該放在一塊兒,不要穿插進行。由於連續地設置元素樣式(寫操做),瀏覽器會一次性執行,即只觸發一次重排或重繪,但若是在幾個寫操做間插入讀取樣式的操做,瀏覽器則不得不當即重排或重繪。
  • 一次性改變樣式。不要一條條地改變樣式,而要經過改變class,或者el.style.csstext屬性。
  • 使用離線DOM來改變元素樣式。好比cloneNode()克隆節點,而後再替換掉元素節點 或者 display:none → 改動 → 顯示
  • 儘可能修改層級較低的DOM。
  • 不要在循環中重複讀取DOM節點屬性值。
  • 使用 window.requestAnimationFrame()、window.requestIdleCallback() 這兩個方法控制從新渲染。

提升fps(frame per second)

網頁動畫的每一幀(frame)都是一次從新渲染,將一幀送到屏幕會採用以下順序:

幀數與動畫流暢度的關係以下:

  • < 24幀 :人眼能感覺到停頓
  • 30 - 60 幀數:比較流暢
  • 70 - 80:德芙,縱享新絲滑 考慮到多數顯示器的刷新頻率爲60Hz,即每秒刷新60次,若是fps高於60,每秒將輸出高於60張的畫面,那麼假如fps > 刷新頻率,多輸出的畫面將是無效的幀數,流暢度沒有任何提高,但也沒啥壞處(手動滑稽d:)。

這裏想說地是fps最好能達到瀏覽器刷新頻率,於是在播放動畫的時候,注意不要執行太多耗時耗性能的操做。

手動控制從新渲染

window.requestAnimationFrame() 方法能夠將某些代碼統一放到下一次從新渲染時執行。具體是將js代碼放在下一幀開始時執行。若是使用setTimeout 或 setInterval 來執行動畫之類的視覺變化,其回調可能在幀的某個時間點執行,可能在末尾,這會使咱們丟失幀,致使卡頓。

  1. 處理「佈局抖動」 反覆讀寫屬性會致使佈局抖動,致使長幀。
function doubleHeight(element) {
    var currentHeight = element.clientWidth;
    element.style.width = (currentHeight / 2) + 'px';
    element.style.height = '80px';
}
var elements = document.getElementsByTagName('tr');
for (var i = 0; i < elements.length; i++) {
    doubleHeight(elements[i]);
}
複製代碼

將doubleHeight函數改爲下面這樣:

function doubleHeight(element) {
    var currentHeight = element.clientHeight;
    window.requestAnimationFrame(function () {
      element.style.height = (currentHeight * 2) + 'px';
    });
}
複製代碼

2)頁面滾動事件(scroll)

$(window).on('scroll', function() {
   window.requestAnimationFrame(scrollHandler);
});
複製代碼

3)最適合用於動畫

結合項目

如今項目中,頁面(以「任務」頁面爲例)在加載時都會請求一些ajax數據,好比datagrid,tree數據等等,還有些ajax數據只是預加載。若是這些ajax在頁面渲染前完成請求,則會阻塞頁面渲染。因此同一個頁面不一樣網速下會有兩種渲染順序:

  • 在渲染以後執行

  • 在渲染以前執行

解決辦法

  1. 在全部資源加載完後進行ajax請求。將datagrid等控件的數據加載放在$(window).load()事件中。
  2. 延遲初始化modal中的內容

效果


問題


問:請求js文件時,請求和執行順序是什麼?

答:請求會一塊兒發出;執行順序按引入的順序,不會由於後一個先返回數據而先執行。

問:頁面加載時提早ajax請求一些數據,會不會影響性能?

答:可能會,可能不會。ajax請求時不影響性能,請求後執行回調函數會影響。ajax回調函數會在請求完成且js主程序運行完後執行,等到咱們能看到頁面,須要經歷頁面解析和渲染這兩個過程,若是頁面渲染前ajax回調執行了,那將阻塞渲染過程。

問:爲何jquery一般在ready方法中執行代碼?

答:ready 監測的是 DOMContentLoaded事件,也就是監測DOM樹構建完成。

附錄


loading 事件

事件 描述
Parse HTML 瀏覽器執行HTML文件解析
Parse Stylesheet 瀏覽器執行CSS文件解析(單指外部CSS文件)
Finish Loading 網絡請求完畢事件
Receive Data 請求的響應數據到達事件,若是響應數據很大(拆包),可能會屢次觸發該事件
Receive Response 響應頭報文到達時觸發
Send Request 發送網絡請求時觸發

Scripting事件

事件 描述
Animation Frame Fired 一個定義好的動畫幀發生並開始回調處理時觸發
Cancel Animation Frame 取消一個動畫幀時觸發
GC Event 垃圾回收時觸發
DOMContentLoaded 當頁面中的DOM內容加載並解析完畢時觸發
Evaluate Script A script was evaluated.
Event js事件
Function Call 只有當瀏覽器進入到js引擎中時觸發
Install Timer 建立計時器(調用setTimeout()和setInterval())時觸發
Request Animation Frame A requestAnimationFrame() call scheduled a new frame
Remove Timer 當清除一個計時器時觸發
Time 調用console.time()觸發
Time End 調用console.timeEnd()觸發
Timer Fired 定時器激活回調後觸發
XHR Ready State Change 當一個異步請求爲就緒狀態後觸發
XHR Load 當一個異步請求完成加載後觸發

Rendering事件

事件 描述
Invalidate layout 當DOM更改致使頁面佈局失效時觸發
Layout 頁面佈局計算執行時觸發
Recalculate style Chrome從新計算元素樣式時觸發
Scroll 內嵌的視窗滾動時觸發

Painting事件

事件 描述
Composite Layers Chrome的渲染引擎完成圖片層合併時觸發
Image Decode 一個圖片資源完成解碼後觸發
Image Resize 一個圖片被修改尺寸後觸發
Paint 合併後的層被繪製到對應顯示區域後觸發
相關文章
相關標籤/搜索