前端性能優化-重繪與重排

  重排和重繪

網頁生成過程:

  1. HTML被HTML解析器解析成DOM 樹
  2. css則被css解析器解析成CSSOM 樹
  3. 結合DOM樹和CSSOM樹,生成一棵渲染樹(Render Tree)
  4. 生成佈局(flow),即將所有渲染樹的所有節點進行平面合成
  5. 將佈局繪製(paint)在屏幕上

第四步和第五步是最耗時的部分,這兩步合起來,就是我們通常所說的渲染。(之前有博客詳細解釋了)

網頁生成的時候,至少會渲染一次

在用戶訪問的過程中,還會不斷重新渲染

重新渲染需要重複之前的第四步(重新生成佈局)+第五步(重新繪製)或者只有第五個步(重新繪製)。

定義

  • 重排是什麼:重新生成佈局。當DOM 的變化影響了元素的幾何屬性(寬和高)--比如改變邊框寬度或給段落增加文字導致行數增加--瀏覽器需要重新計算元素的幾何屬性,同樣其他元素的幾何屬性和位置也會因此受到影響。瀏覽器會使渲染樹中受到影響的部分失效,並重新構造渲染樹。這個過程稱爲重排。

  • 重繪是什麼:重新繪製。完成重排後,瀏覽器會重新繪製受影響的部分到屏幕中。這個過程稱爲重繪。

發生重排的情況

  • 添加或刪除可見的DOM元素
  • 元素位置改變
  • 元素本身的尺寸發生改變
  • 內容改變
  • 頁面渲染器初始化
  • 瀏覽器窗口大小發生改變

重排與重繪的關係

重排一定會導致重繪,重繪不一定導致重排。如果DOM變化不影響幾何屬性,元素的佈局沒有改變,則只發生一次重繪(不需要重排)。

對性能的影響

重排和重繪會不斷觸發,這是不可避免的。但是,它們非常耗費資源,是導致網頁性能低下的根本原因。

提高網頁性能,就是要降低"重排"和"重繪"的頻率和成本,儘量少觸發重新渲染。

渲染樹變化的排隊

前面提到,DOM變動和樣式變動,都會觸發重新渲染。但是,瀏覽器已經很智能了,會盡量把所有的變動集中在一起,排成一個隊列,然後一次性執行,儘量避免多次重新渲染。

例子:

div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';

根據我們上文的定義,這段代碼理論上會觸發4次重排+重繪,因爲每一次都改變了元素的幾何屬性,實際上最後只觸發了一次重排,我們修改了元素的幾何屬性,導致瀏覽器觸發重排或重繪時。它會把該操作放進渲染隊列,等到隊列中的操作到了一定的數量或者到了一定的時間間隔時,瀏覽器就會批量執行這些操作。

強制刷新隊列:

div.style.left = '10px';
console.log(div.offsetLeft);
div.style.top = '10px';
console.log(div.offsetTop);
div.style.width = '20px';
console.log(div.offsetWidth);
div.style.height = '20px';
console.log(div.offsetHeight);

這段代碼會觸發4次重排+重繪,因爲在console中你請求的這幾個樣式信息,無論何時瀏覽器都會立即執行渲染隊列的任務,即使該值與你操作中修改的值沒關聯。

因爲隊列中,可能會有影響到這些值的操作,爲了給我們最精確的值,瀏覽器會立即重排+重繪

強制刷新隊列的style樣式請求

  1. offsetTop, offsetLeft, offsetWidth, offsetHeight
  2. scrollTop, scrollLeft, scrollWidth, scrollHeight
  3. clientTop, clientLeft, clientWidth, clientHeight
  4. getComputedStyle(), 或者 IE的 currentStyle

我們在開發中,應該謹慎的使用這些style請求,注意上下文關係,避免一行代碼一個重排,這對性能是個巨大的消耗!!!

 

性能優化:

1. 分離讀寫操作

div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
console.log(div.offsetLeft);
console.log(div.offsetTop);
console.log(div.offsetWidth);
console.log(div.offsetHeight);
 

發生一次重排,原因上面有寫,所有的讀操作放在寫操作之後

2. 樣式集中改變

div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';

雖然現在大部分瀏覽器有渲染隊列優化,不排除有些瀏覽器以及老版本的瀏覽器效率仍然低下:

建議通過改變class或者csstext屬性集中改變樣式,這種辦法可維護性好,還可以幫助我們免除顯示性代碼 
有很小的性能影響,改變class需要檢查級聯樣式,但是符合BEM標準可以更好的解耦,增加可維護性,建議這樣寫

//例
// 獲取el類型 
el.className += " theclassname";
// good
el.style.cssText += "; left: " + left + "px; top: " + top + "px;"; 

3. 緩存佈局信息

// 強制刷新 觸發兩次重排
    div.style.left = div.offsetLeft + 1 + 'px';
    div.style.top = div.offsetTop + 1 + 'px';
//緩存佈局信息 讀寫分離
  var curLeft = div.offsetLeft;
    var curTop = div.offsetTop;
    div.style.left = curLeft + 1 + 'px';
    div.style.top = curTop + 1 + 'px';

4. 離線改變dom

隱藏要操作的dom

在要操作dom之前,通過display隱藏dom,當操作完成之後,纔將元素的display屬性爲可見,因爲不可見的元素不會觸發重排和重繪。

通過使用DocumentFragment創建一個dom碎片,在它上面批量操作dom,操作完成之後,再添加到文檔中,這樣只會觸發一次重排。

5. position屬性爲absolute或fixed

position屬性爲absolute或fixed的元素,重排開銷比較小,不用考慮它對其他元素的影響

6. 優化動畫

可以把動畫效果應用到position屬性爲absolute或fixed的元素上,這樣對其他元素影響較小

 

 

 

 

 

 

 

 

 

 

發佈了48 篇原創文章 · 獲贊 42 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章