由於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、提高性能的方法。