首页 » 编写高质量代码:改善JavaScript程序的188个建议 » 编写高质量代码:改善JavaScript程序的188个建议全文在线阅读

《编写高质量代码:改善JavaScript程序的188个建议》建议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、提高性能的方法。