讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議179:減少DOM操作中的Repaint和Reflow >

建議179:減少DOM操作中的Repaint和Reflow

由於JavaScript開發離不開DOM操作,所以對DOM操作的性能優化在Web開發中是非常重要的。Repaint(或稱為Redraw)是一種不會影響當前DOM結構和佈局的一種重繪動作。下面動作都會產生Repaint動作:

❑不可見到可見(visibility樣式屬性)。

❑顏色或圖片變化(background、border-color、color樣式屬性)。

❑不改變頁面元素大小、形狀和位置,但改變其外觀的變化。

Reflow主要發生在DOM樹被操作的時候。任何改變DOM的結構和佈局的操作都會產生Reflow。當一個元素的Reflow操作發生時,它的所有父元素和子元素都會產生Reflow,最後Reflow必然會導致Repaint的產生。例如,如下動作會產生Repaint動作:

❑瀏覽器窗口的變化。

❑DOM節點的添加和刪除操作。

❑一些改變頁面元素大小、形狀和位置的操作的觸發。

每次Reflow會比Repaint帶來更多的資源消耗,應該盡量減少Reflow的發生,或者將其轉化為只會觸發Repaint操作的代碼,例如:


var pDiv=document.createElement("p");

document.body.appendChild(pDiv);//Reflow

var cDiv1=document.createElement("p");

var cDiv2=document.createElement("p");

pDiv.appendChild(cDiv1);//Reflow

pDiv.appendChild(cDiv2);//Reflow


在上面代碼中,總共產生3次Reflow。下面進行優化:


var pDiv=document.createElement("p");

var cDiv1=document.createElement("p");

var cDiv2=document.createElement("p");

pDiv.appendChild(cDiv1);

pDiv.appendChild(cDiv2);

document.body.appendChild(pDiv);//Reflow


這樣只有一次Reflow,因此,推薦這種DOM節點操作的方式。

關於上述較少Reflow操作的解決方案,還有一種可以參考的模式:利用display減少Reflow。


var pDiv=document.getElementById("parent");

pDiv.style.display="none";//reflow

pDiv.appendChild(cDiv1);

pDiv.appendChild(cDiv2);

pDiv.appendChild(cDiv3);

pDiv.appendChild(cDiv4);

pDiv.appendChild(cDiv5);

pDiv.style.;

pDiv.style.;

pDiv.style.display="block";//reflow


先隱藏pDiv,再顯示,這樣隱藏和顯示之間的操作便不會產生任何的Reflow,提高了效率。

DOM元素中有一些特殊的測量屬性的訪問和方法的調用,也會觸發Reflow,比較典型的就是offsetWidth屬性和getComputedStyle方法。例如,下面代碼都會產生Reflow。


var width=element.offsetWidth;

var scrollleft=element.scrollleft;

var display=window.getComputerStyle(p,'').getPropertyValue('display');


這些測量屬性和方法如下:

❑offsetLeft

❑offsetTop

❑offsetHeight

❑offsetWidth

❑scrollTop/Left/Width/Height

❑clientTop/Left/Width/Height

❑getComputedStyle

❑currentStyle(in IE)

對這些測量屬性和方法的訪問或調用都會觸發Reflow的產生,應該盡量減少對這些屬性和方法的訪問或調用。


var pe=document.getElementById("pos_element");

var result=document.getElementById("result_element");

var pOffsetWidth=pe.offsetWidth;

result.children[0].style.width=pOffsetWidth;

result.children[1].style.width=pOffsetWidth;

result.children[2].style.width=pOffsetWidth;


在上面代碼中,使用臨時變量將offsetWidth的值緩存起來,這樣就不用每次都訪問offsetWidth屬性。這種方式在循環中非常適用,可以極大地提高性能。

經常見到如下的代碼:


var sElement=document.getElementById("pos_element");

sElement.style.border='1px solid red'

sElement.style.backgroundColor='silver'

sElement.style.padding='2px 3px'

sElement.style.marginLeft='5px'


從中可以看到,這裡的每一個樣式的改變,都會產生Reflow。要減少這種情況的發生,可以這樣做:


.class1{

border:'1px solid red'

background-color:'silver'

padding:'2px 3px'

margin-left:'5px'

}

document.getElementById("pos_element").className='class1';


用class替代style,可以將原有的所有Reflow或Repaint的次數都縮減到一次。


var sElement=document.getElementById("pos_element");

var newStyle='border:1px solid red;'+'background-color:silver;'+'padding:2px 3px;'+"margin-left:5px;"

sElement.style.cssText+=newStyle;


一次性設置所有樣式,也是減少Reflow、提高性能的方法。