前端性能之迴流與重繪(reflow && repaint)

寫在金三銀四之際。
由於種種緣由想要謀求新的發展,不得已翻起了塵封已久的高程書;寫起了各類經典CSS佈局;回顧起記憶略顯模糊的幾個項目。感慨着太多太多的知識點本身都不夠深刻甚至缺少認識,卻又不能急功近利,囫圇吞棗。
牢騷發完了,苦水吐完了,進入正題。

萬惡之源——無知

迴流(reflow),重繪(repaint)都是瀏覽器更新頁面視圖的方式,區別在於:javascript

  • 對於元素視覺上的改變(如改變outline,background-color,visibility等)會觸發repaint;
  • 對於元素佈局上的改變(增刪節點,觸發resize事件,修改style屬性等)會觸發reflow;

repaint和reflow是DOM操做影響性能的主要緣由。一個節點觸發了repaint,瀏覽器會檢查DOM Tree中其餘全部節點的顯示方式;一個節點觸發了reflow會致使它的祖先節點,後代節點以及在它以後的節點所有reflow。reflow對性能的影響大於repaint。css

一個前端開發人員對這些概念一無所知是一件很恐怖的事情,想一想他在寫代碼的時候不知道哪些操做會對性能形成影響,可能會出現這種狀況:前端

var toChange = document.getElementById('target');
toChange.style.background = '#333';
toChange.style.color = '#fff';
toChange.style.border = '1px solid #00f';

這無疑是能夠優化的,下面咱們來看看如何減小reflow,repaint的次數。java

解決方式——控制

咱們無力去改變repaint,reflow對性能損害的程度,咱們能作的只有減小它們發生的次數。瀏覽器

脫離

動畫時時刻刻都在操做着DOM,爲了不動畫使得其餘節點也在時時刻刻reflow,能夠將動畫所在的元素設爲position: fixed或者position: absolute,使其脫離文檔流,這個元素reflow時不會影響其餘節點的佈局,雖然仍是會產生repaint,但相對來講獲得了優化。app

合併

能一次完成的操做就不要分兩次。好比說添加多個節點時使用DocumentFragment:函數

var docFragm = document.createDocumentFragment();
var elem, contents;
for(var i = 0; i < textlist.length; i++) {
    elem = document.createElement('p');
    contents = document.createTextNode(textlist[i]);
    elem.appendChild(contents);
    docFragm.appendChild(elem);
}
document.body.appendChild(docFragm);

再好比像上面style那個例子,若是已知變化後樣式,將這些樣式寫成一個class,再去改變元素的class或者直接修改cssText屬性,這兩種方式都將屢次reflow縮減爲一次。佈局

複製

對複製品進行操做也是一種解決方案,好比須要對已有節點進行DOM操做使用cloneNode():性能

var original = document.getElementById('container');
var cloned = original.cloneNode(true);
cloned.setAttribute('width', '50%');
var elem, contents;
for(var i = 0; i < textlist.length; i++) {
    elem = document.createElement('p');
    contents = document.createTextNode(textlist[i]);
    elem.appendChild(contents);
    cloned.appendChild(elem);
}
original.parentNode.replaceChild(cloned, original);

使用cloneNode()要注意的是,惟一的參數表明是否進行深複製。另外cloneNode()沒法複製事件監聽函數,以及表單控件的value。
又好比獲取offsetWidth,getComputedStyle()這些取值操做每次都會觸發reflow,在第一次調用時存起來也是複製的一種。優化

捨棄

在作好了其餘優化措施的前提下想要進一步提高性能有時須要捨棄,好比減少動畫幀數,即增大動畫函數執行的間隔,這樣動畫流暢程度會下降,但整個應用的性能獲得了提高。

其餘
  • 減小table的使用,table形成的reflow是其餘塊級元素的三倍;
  • 儘可能避免改變DOM Tree中高層節點的class,減少reflow的影響;
  • 利用各類選擇器規則減小待操做節點的數量;

最後,我認爲理解reflow和repaint的原理及觸發狀況是十分重要的,在寫每一行代碼時都應該明確它對性能的影響。

參考文章:
REFLOWS & REPAINTS: CSS PERFORMANCE MAKING YOUR JAVASCRIPT SLOW?

Effecitve JavaScript

相關文章
相關標籤/搜索